New drop-menu for picking a standard bit size.
This commit is contained in:
@@ -1,91 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import TextField from "@smui/textfield";
|
|
||||||
import Select, { Option } from "@smui/select";
|
|
||||||
import type { MenuComponentDev } from "@smui/menu";
|
|
||||||
import Menu from "@smui/menu";
|
|
||||||
import List, { Item, Text } from "@smui/list";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { set_input_value } from "svelte/internal";
|
|
||||||
|
|
||||||
let menu: MenuComponentDev;
|
|
||||||
|
|
||||||
type Option = {
|
|
||||||
value: number;
|
|
||||||
label: string;
|
|
||||||
metric: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export let label: string;
|
|
||||||
export let value: number;
|
|
||||||
export let metric: boolean;
|
|
||||||
export let options: Option[];
|
|
||||||
|
|
||||||
let textValue = "";
|
|
||||||
|
|
||||||
$: if (textValue) {
|
|
||||||
value = Number.parseFloat(textValue) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
textValue = value?.toString() || "";
|
|
||||||
});
|
|
||||||
|
|
||||||
function onOptionSelected(option: Option) {
|
|
||||||
textValue = option.value.toString();
|
|
||||||
metric = option.metric;
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterKeys(event) {
|
|
||||||
const input = event.target;
|
|
||||||
|
|
||||||
if (input.value.match(/[^0-9.]/)) {
|
|
||||||
input.value = input.value.replace(/[^0-9.]/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.value.match(/\.[^.]*\./)) {
|
|
||||||
input.value = input.value.replace(/(\.[^.]*)\./g, "$1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="value-and-unit">
|
|
||||||
<TextField
|
|
||||||
{label}
|
|
||||||
on:keypress={filterKeys}
|
|
||||||
on:keyup={filterKeys}
|
|
||||||
bind:value={textValue}
|
|
||||||
on:click={() => menu.setOpen(true)}
|
|
||||||
/>
|
|
||||||
<Select bind:value={metric}>
|
|
||||||
<Option value={true}>mm</Option>
|
|
||||||
<Option value={false}>in</Option>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<Menu bind:this={menu} anchorCorner="BOTTOM_LEFT">
|
|
||||||
<List>
|
|
||||||
{#each options as option}
|
|
||||||
<Item on:SMUI:action={() => onOptionSelected(option)}>
|
|
||||||
<Text>{option.label}</Text>
|
|
||||||
</Item>
|
|
||||||
{/each}
|
|
||||||
</List>
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
:global {
|
|
||||||
.value-and-unit {
|
|
||||||
display: flex;
|
|
||||||
column-gap: 10px;
|
|
||||||
|
|
||||||
.mdc-select {
|
|
||||||
max-width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smui-select--standard .mdc-select__dropdown-icon {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import TextField from "@smui/textfield";
|
||||||
|
import Icon from "@smui/textfield/icon";
|
||||||
|
import HelperText from "@smui/textfield/helper-text";
|
||||||
|
import MenuSurface, {
|
||||||
|
type MenuSurfaceComponentDev,
|
||||||
|
} from "@smui/menu-surface";
|
||||||
|
import List, { Item, Text } from "@smui/list";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
|
let menuSurface: MenuSurfaceComponentDev;
|
||||||
|
let menuTimeout;
|
||||||
|
let optionSelected: boolean = false;
|
||||||
|
|
||||||
|
export let value: string;
|
||||||
|
export let options: string[][];
|
||||||
|
export let valid: boolean;
|
||||||
|
export let helperText: string;
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (menuTimeout) {
|
||||||
|
clearTimeout(menuTimeout);
|
||||||
|
menuTimeout = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showMenu(show: boolean) {
|
||||||
|
if (show && optionSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionSelected = false;
|
||||||
|
|
||||||
|
if (menuTimeout) {
|
||||||
|
clearTimeout(menuTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a timeout to "debounce" the display of the menu.
|
||||||
|
menuTimeout = setTimeout(() => menuSurface.setOpen(show), 100);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="textfield-with-options">
|
||||||
|
<TextField
|
||||||
|
bind:value
|
||||||
|
on:focusin={() => showMenu(true)}
|
||||||
|
on:focusout={() => showMenu(false)}
|
||||||
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<div slot="trailingIcon">
|
||||||
|
{#if valid}
|
||||||
|
<Icon class="fa fa-check-circle-o" style="color: green;" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<HelperText persistent slot="helper">{helperText}</HelperText>
|
||||||
|
</TextField>
|
||||||
|
|
||||||
|
<MenuSurface bind:this={menuSurface} anchorCorner="BOTTOM_LEFT">
|
||||||
|
<div style="display: flex; flex-direction: row;">
|
||||||
|
{#each options as group}
|
||||||
|
<List>
|
||||||
|
{#each group as option}
|
||||||
|
<Item
|
||||||
|
on:SMUI:action={() => {
|
||||||
|
value = option;
|
||||||
|
showMenu(false);
|
||||||
|
|
||||||
|
optionSelected = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>{option}</Text>
|
||||||
|
</Item>
|
||||||
|
{/each}
|
||||||
|
</List>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</MenuSurface>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
:global {
|
||||||
|
.textfield-with-options {
|
||||||
|
.mdc-deprecated-list-item {
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
import * as api from "$lib/api";
|
import * as api from "$lib/api";
|
||||||
import { virtualKeyboardChangeHelper } from "$lib/customActions";
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
||||||
//
|
//
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<Content id="change-hostname-dialog-content">
|
<Content id="change-hostname-dialog-content">
|
||||||
<TextField
|
<TextField
|
||||||
bind:value={hostname}
|
bind:value={hostname}
|
||||||
use={[[virtualKeyboardChangeHelper, (newValue) => (hostname = newValue)]]}
|
use={[[virtualKeyboardChange, (newValue) => (hostname = newValue)]]}
|
||||||
label="New Hostname"
|
label="New Hostname"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
import { virtualKeyboardChangeHelper } from "$lib/customActions";
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let axis = "";
|
export let axis = "";
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
label="Absolute"
|
label="Absolute"
|
||||||
type="number"
|
type="number"
|
||||||
bind:value
|
bind:value
|
||||||
use={[[virtualKeyboardChangeHelper, (newValue) => (value = newValue)]]}
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
variant="filled"
|
variant="filled"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script type="ts">
|
<script type="ts">
|
||||||
import DimensionInput from "$components/DimensionInput.svelte";
|
|
||||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { waitForChange } from "$lib/StoreHelpers";
|
import { waitForChange } from "$lib/StoreHelpers";
|
||||||
@@ -13,14 +12,23 @@
|
|||||||
probingFailed,
|
probingFailed,
|
||||||
probingStarted,
|
probingStarted,
|
||||||
} from "$lib/ControllerState";
|
} from "$lib/ControllerState";
|
||||||
|
import { numberWithUnit } from "$lib/RegexHelpers";
|
||||||
|
import TextFieldWithOptions from "$components/TextFieldWithOptions.svelte";
|
||||||
|
|
||||||
type Step =
|
const ValidSteps = [
|
||||||
| "None"
|
"None",
|
||||||
| "CheckProbe"
|
"CheckProbe",
|
||||||
| "BitDimensions"
|
"BitDimensions",
|
||||||
| "PlaceProbeBlock"
|
"PlaceProbeBlock",
|
||||||
| "Probe"
|
"Probe",
|
||||||
| "Done";
|
"Done",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
type Step = typeof ValidSteps[number];
|
||||||
|
|
||||||
|
function isStep(str): str is Step {
|
||||||
|
return ValidSteps.includes(str);
|
||||||
|
}
|
||||||
|
|
||||||
const stepLabels: Record<Step, string> = {
|
const stepLabels: Record<Step, string> = {
|
||||||
None: "",
|
None: "",
|
||||||
@@ -34,21 +42,16 @@
|
|||||||
const cancelled = writable(false);
|
const cancelled = writable(false);
|
||||||
const userAcknowledged = writable(false);
|
const userAcknowledged = writable(false);
|
||||||
|
|
||||||
const cutterDiameterOptions = [
|
const imperialBits = ["1/2 in", "1/4 in", "1/8 in"];
|
||||||
{ value: 0.5, label: '1/2 "', metric: false },
|
const metricBits = ["10 mm", "8 mm", "6 mm", "3 mm"];
|
||||||
{ value: 0.25, label: '1/4 "', metric: false },
|
|
||||||
{ value: 0.125, label: '1/8 "', metric: false },
|
|
||||||
{ value: 10, label: "10 mm", metric: true },
|
|
||||||
{ value: 6, label: "6 mm", metric: true },
|
|
||||||
{ value: 3, label: "10 mm", metric: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
export let open;
|
export let open;
|
||||||
export let probeType: "xyz" | "z";
|
export let probeType: "xyz" | "z";
|
||||||
let currentStep: Step = "None";
|
let currentStep: Step = "None";
|
||||||
let cutterDiameter: number;
|
let cutterDiameterString: string = "";
|
||||||
|
let cutterDiameterMetric: number;
|
||||||
let showCancelButton = true;
|
let showCancelButton = true;
|
||||||
let steps: Array<Step> = [];
|
let steps: Step[] = [];
|
||||||
let nextButton = {
|
let nextButton = {
|
||||||
label: "Next",
|
label: "Next",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@@ -56,10 +59,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$: metric = $Config.settings?.units === "METRIC";
|
$: metric = $Config.settings?.units === "METRIC";
|
||||||
|
$: cutterDiameterMetric = numberWithUnit
|
||||||
|
.parse(cutterDiameterString)
|
||||||
|
?.toMetric();
|
||||||
|
|
||||||
$: if (open) {
|
$: if (open) {
|
||||||
cutterDiameter =
|
cutterDiameterString = localStorage.getItem("cutterDiameter") ?? "";
|
||||||
Number.parseFloat(localStorage.getItem("cutterDiameter")) || null;
|
|
||||||
|
|
||||||
// Svelte appears not to like it when you invoke
|
// Svelte appears not to like it when you invoke
|
||||||
// an async function from a reactive statement, so we
|
// an async function from a reactive statement, so we
|
||||||
@@ -67,32 +72,31 @@
|
|||||||
requestAnimationFrame(begin);
|
requestAnimationFrame(begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (cutterDiameter) {
|
$: if (cutterDiameterString) {
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeSkippedSteps(steps: Step[]): Step[] {
|
|
||||||
return steps.filter((x) => x);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function begin() {
|
async function begin() {
|
||||||
try {
|
try {
|
||||||
$probingActive = true;
|
$probingActive = true;
|
||||||
assertValidProbeType();
|
assertValidProbeType();
|
||||||
|
|
||||||
steps = removeSkippedSteps([
|
steps = [
|
||||||
"CheckProbe",
|
"CheckProbe",
|
||||||
probeType === "xyz" ? "BitDimensions" : undefined,
|
probeType === "xyz" ? "BitDimensions" : undefined,
|
||||||
"PlaceProbeBlock",
|
"PlaceProbeBlock",
|
||||||
"Probe",
|
"Probe",
|
||||||
"Done",
|
"Done",
|
||||||
]);
|
].filter<Step>(isStep);
|
||||||
|
|
||||||
await stepCompleted("CheckProbe", probeContacted);
|
await stepCompleted("CheckProbe", probeContacted);
|
||||||
|
|
||||||
if (probeType === "xyz") {
|
if (probeType === "xyz") {
|
||||||
await stepCompleted("BitDimensions", userAcknowledged);
|
await stepCompleted("BitDimensions", userAcknowledged);
|
||||||
localStorage.setItem("cutterDiameter", cutterDiameter.toString());
|
localStorage.setItem(
|
||||||
|
"cutterDiameter",
|
||||||
|
numberWithUnit.normalize(cutterDiameterString)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
||||||
@@ -177,11 +181,7 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "BitDimensions":
|
case "BitDimensions":
|
||||||
nextButton.disabled = !(
|
nextButton.disabled = !isFinite(cutterDiameterMetric);
|
||||||
cutterDiameter !== null &&
|
|
||||||
cutterDiameter !== 0 &&
|
|
||||||
isFinite(cutterDiameter)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Done":
|
case "Done":
|
||||||
@@ -204,8 +204,8 @@
|
|||||||
|
|
||||||
const cutterLength = 12.7;
|
const cutterLength = 12.7;
|
||||||
const zLift = 1;
|
const zLift = 1;
|
||||||
const xOffset = probeBlockWidth + cutterDiameter / 2.0;
|
const xOffset = probeBlockWidth + cutterDiameterMetric / 2.0;
|
||||||
const yOffset = probeBlockLength + cutterDiameter / 2.0;
|
const yOffset = probeBlockLength + cutterDiameterMetric / 2.0;
|
||||||
const zOffset = probeBlockHeight;
|
const zOffset = probeBlockHeight;
|
||||||
|
|
||||||
if (probeType === "z") {
|
if (probeType === "z") {
|
||||||
@@ -269,11 +269,11 @@
|
|||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="probe-dialog-title"
|
aria-labelledby="probe-dialog-title"
|
||||||
aria-describedby="probe-dialog-content"
|
aria-describedby="probe-dialog-content"
|
||||||
surface$style="width: 700px; height: 400px; max-width: calc(100vw - 32px); overflow: visible;"
|
surface$style="width: 700px; max-width: calc(100vw - 32px);"
|
||||||
>
|
>
|
||||||
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
||||||
|
|
||||||
<Content id="probe-dialog-content" style="overflow: visible;">
|
<Content id="probe-dialog-content">
|
||||||
<div class="steps">
|
<div class="steps">
|
||||||
<p><b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b></p>
|
<p><b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -282,48 +282,49 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="current-step">
|
<p>
|
||||||
<p>
|
{#if currentStep === "CheckProbe"}
|
||||||
{#if currentStep === "CheckProbe"}
|
Attach the probe magnet to the collet, then touch the probe block to the
|
||||||
Attach the probe magnet to the collet, then touch the probe block to
|
bit.
|
||||||
the bit.
|
{:else if currentStep === "BitDimensions"}
|
||||||
{:else if currentStep === "BitDimensions"}
|
<TextFieldWithOptions
|
||||||
<DimensionInput
|
label="Cutter diameter"
|
||||||
label="Cutter diameter"
|
variant="filled"
|
||||||
options={cutterDiameterOptions}
|
spellcheck="false"
|
||||||
bind:value={cutterDiameter}
|
style="width: 100%;"
|
||||||
{metric}
|
bind:value={cutterDiameterString}
|
||||||
/>
|
options={[imperialBits, metricBits]}
|
||||||
{:else if currentStep === "PlaceProbeBlock"}
|
valid={isFinite(cutterDiameterMetric)}
|
||||||
|
helperText={`Examples: 1/2", 10 mm, 0.25 in`}
|
||||||
|
/>
|
||||||
|
{:else if currentStep === "PlaceProbeBlock"}
|
||||||
|
{#if probeType === "xyz"}
|
||||||
|
Place the probe block face up, on the lower-left corner of your
|
||||||
|
workpiece.
|
||||||
|
{:else}
|
||||||
|
Place the probe block face down, with the bit above the recess.
|
||||||
|
{/if}
|
||||||
|
{:else if currentStep === "Probe"}
|
||||||
|
Probing in progress...
|
||||||
|
{:else if currentStep === "Done"}
|
||||||
|
{#if $probingFailed}
|
||||||
|
Could not find the probe block during probing!
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Make sure the tip of the bit is less than {metric ? "25mm" : "1 in"}
|
||||||
|
above the probe block, and try again.
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
|
Don't forget to put away the probe!
|
||||||
|
|
||||||
{#if probeType === "xyz"}
|
{#if probeType === "xyz"}
|
||||||
Place the probe block face up, on the lower-left corner of your
|
<p>The machine will now move to the XY origin.</p>
|
||||||
workpiece.
|
|
||||||
{:else}
|
|
||||||
Place the probe block face down, with the bit above the recess.
|
|
||||||
{/if}
|
|
||||||
{:else if currentStep === "Probe"}
|
|
||||||
Probing in progress...
|
|
||||||
{:else if currentStep === "Done"}
|
|
||||||
{#if $probingFailed}
|
|
||||||
Could not find the probe block during probing!
|
|
||||||
|
|
||||||
<p>
|
<p>Watch your hands!</p>
|
||||||
Make sure the tip of the bit is less than {metric
|
|
||||||
? "25mm"
|
|
||||||
: "1 in"} above the probe block, and try again.
|
|
||||||
</p>
|
|
||||||
{:else}
|
|
||||||
Don't forget to put away the probe!
|
|
||||||
|
|
||||||
{#if probeType === "xyz"}
|
|
||||||
<p>The machine will now move to the XY origin.</p>
|
|
||||||
|
|
||||||
<p>Watch your hands!</p>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
{/if}
|
||||||
</div>
|
</p>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
@@ -358,12 +359,9 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-step {
|
.bit-dimensions {
|
||||||
flex-grow: 1;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
.mdc-text-field {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.steps {
|
.steps {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
import { virtualKeyboardChangeHelper } from "$lib/customActions";
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let axis = "";
|
export let axis = "";
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
label="Position"
|
label="Position"
|
||||||
type="number"
|
type="number"
|
||||||
bind:value
|
bind:value
|
||||||
use={[[virtualKeyboardChangeHelper, (newValue) => (value = newValue)]]}
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import CircularProgress from "@smui/circular-progress";
|
import CircularProgress from "@smui/circular-progress";
|
||||||
import VirtualList from "svelte-tiny-virtual-list";
|
import VirtualList from "svelte-tiny-virtual-list";
|
||||||
import * as api from "$lib/api";
|
import * as api from "$lib/api";
|
||||||
import { virtualKeyboardChangeHelper } from "$lib/customActions";
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
const itemHeight = 35;
|
const itemHeight = 35;
|
||||||
|
|
||||||
@@ -154,9 +154,7 @@
|
|||||||
<Label>Date & Time</Label>
|
<Label>Date & Time</Label>
|
||||||
<TextField
|
<TextField
|
||||||
bind:value
|
bind:value
|
||||||
use={[
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
[virtualKeyboardChangeHelper, (newValue) => (value = newValue)],
|
|
||||||
]}
|
|
||||||
label="Time"
|
label="Time"
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
import type { WifiNetwork } from "$lib/NetworkInfo";
|
import type { WifiNetwork } from "$lib/NetworkInfo";
|
||||||
import * as api from "$lib/api";
|
import * as api from "$lib/api";
|
||||||
import { virtualKeyboardChangeHelper } from "$lib/customActions";
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
export let network: WifiNetwork;
|
export let network: WifiNetwork;
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
{#if needPassword}
|
{#if needPassword}
|
||||||
<TextField
|
<TextField
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
use={[[virtualKeyboardChangeHelper, (newValue) => (password = newValue)]]}
|
use={[[virtualKeyboardChange, (newValue) => (password = newValue)]]}
|
||||||
label="Password"
|
label="Password"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
|||||||
9
src/svelte-components/src/lib/CustomActions.ts
Normal file
9
src/svelte-components/src/lib/CustomActions.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export function virtualKeyboardChange(node: HTMLElement, cb: (value: any) => void) {
|
||||||
|
const input = node.querySelector("input");
|
||||||
|
if (!input) {
|
||||||
|
console.error("Could not find the textfield's <input>:", node);
|
||||||
|
throw new Error("Could not find the textfield's <input>");
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener("keyup", () => cb(input.value));
|
||||||
|
}
|
||||||
90
src/svelte-components/src/lib/RegexHelpers.ts
Normal file
90
src/svelte-components/src/lib/RegexHelpers.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
type NumberWithUnit = {
|
||||||
|
value: number,
|
||||||
|
metric: boolean,
|
||||||
|
toMetric: () => number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberWithUnitToMetric() {
|
||||||
|
return this.metric ? this.value : this.value * 25.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPojo(value) {
|
||||||
|
if (value === null || typeof value !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.getPrototypeOf(value) === Object.prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fractions = [
|
||||||
|
{ value: 0.75, formatted: "3/4" },
|
||||||
|
{ value: 0.625, formatted: "5/8" },
|
||||||
|
{ value: 0.5, formatted: "1/2" },
|
||||||
|
{ value: 0.375, formatted: "3/8" },
|
||||||
|
{ value: 0.25, formatted: "1/4" },
|
||||||
|
{ value: 0.1875, formatted: "3/16" },
|
||||||
|
{ value: 0.125, formatted: "1/8" },
|
||||||
|
{ value: 0.09375, formatted: "3/32" },
|
||||||
|
{ value: 0.0625, formatted: "1/16" },
|
||||||
|
{ value: 0.03125, formatted: "1/32" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function formatFraction(value: number) {
|
||||||
|
const fraction = fractions.find(f => f.value === value);
|
||||||
|
|
||||||
|
return fraction ? fraction.formatted : value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const numberWithUnit = {
|
||||||
|
regex: /^\s*(?:(\d+)\s*\/\s*(\d+)|(\d*\.\d+)|(\d+(?:\.\d+)?))\s*("|in|inch|inches|mm|millimeters)\s*$/,
|
||||||
|
parse: function (str: string) {
|
||||||
|
let [, numerator, denominator, decimal1, decimal2, unit]: any = str?.match(numberWithUnit.regex) ?? [];
|
||||||
|
|
||||||
|
numerator = Number.parseFloat(numerator)
|
||||||
|
denominator = Number.parseFloat(denominator)
|
||||||
|
decimal1 = Number.parseFloat(decimal1)
|
||||||
|
decimal2 = Number.parseFloat(decimal2)
|
||||||
|
|
||||||
|
const metric = (unit ?? "").includes("m");
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case isFinite(numerator) && isFinite(denominator):
|
||||||
|
return {
|
||||||
|
value: numerator / denominator,
|
||||||
|
metric,
|
||||||
|
toMetric: numberWithUnitToMetric
|
||||||
|
};
|
||||||
|
|
||||||
|
case isFinite(decimal1) && decimal1 !== 0:
|
||||||
|
return {
|
||||||
|
value: decimal1,
|
||||||
|
metric,
|
||||||
|
toMetric: numberWithUnitToMetric
|
||||||
|
};
|
||||||
|
|
||||||
|
case isFinite(decimal2) && decimal2 !== 0:
|
||||||
|
return {
|
||||||
|
value: decimal2,
|
||||||
|
metric,
|
||||||
|
toMetric: numberWithUnitToMetric
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
normalize: function (str) {
|
||||||
|
const value = this.parse(str);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case !isPojo(value):
|
||||||
|
return "";
|
||||||
|
|
||||||
|
case value.metric:
|
||||||
|
return `${value.value} mm`
|
||||||
|
|
||||||
|
default:
|
||||||
|
return `${formatFraction(value.value)} in`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export function virtualKeyboardChangeHelper(node: HTMLElement, cb: (value: any) => void) {
|
|
||||||
const input = node.querySelector("input");
|
|
||||||
input.addEventListener("keyup", () => cb(input.value));
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,6 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
minify: false,
|
|
||||||
target: "chrome60",
|
target: "chrome60",
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'src/main.ts'),
|
entry: resolve(__dirname, 'src/main.ts'),
|
||||||
|
|||||||
Reference in New Issue
Block a user