-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from twm/widgets
Improved form widgets
- Loading branch information
Showing
12 changed files
with
307 additions
and
97 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<script lang="ts"> | ||
import { frac, parseFrac } from "$lib/frac" | ||
function validate( | ||
input: HTMLInputElement, | ||
v: number, | ||
min: number | null, | ||
max: number | null | ||
) { | ||
if (isNaN(v)) { | ||
input.setCustomValidity("Enter a mixed fraction or decimal number") | ||
return false | ||
} | ||
if (min != null && v < min) { | ||
input.setCustomValidity(`Must be at least ${frac(min)}`) | ||
return false | ||
} | ||
if (max != null && v > max) { | ||
input.setCustomValidity(`Must not exceed ${frac(max)}`) | ||
return false | ||
} | ||
input.setCustomValidity("") | ||
return true | ||
} | ||
export let id: string | ||
export let value: number | ||
export let min: number | null = null | ||
export let max: number | null = null | ||
export let required: boolean = false | ||
let focused: boolean = false | ||
let input: HTMLInputElement | ||
let rawValue = frac(value) | ||
let displayValue = frac(value) + '"' | ||
$: { | ||
// Round to the nearest 1⁄32nd to match the masked value. | ||
let v = Math.round(parseFrac(rawValue) * 32) / 32 | ||
if (input) { | ||
if (validate(input, v, min, max)) { | ||
value = v | ||
displayValue = frac(v) + '"' | ||
} else { | ||
displayValue = rawValue | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<span class="range"> | ||
<input | ||
type="text" | ||
{id} | ||
value={focused ? rawValue : displayValue} | ||
bind:this={input} | ||
{required} | ||
on:input={() => { | ||
rawValue = input.value | ||
}} | ||
on:focus={() => { | ||
focused = true | ||
}} | ||
on:blur={() => { | ||
focused = false | ||
}} | ||
/> | ||
</span> | ||
|
||
<style> | ||
input { | ||
box-sizing: border-box; | ||
width: 100%; | ||
background: inherit; | ||
color: inherit; | ||
border: none; | ||
border-bottom: 1px solid currentColor; | ||
padding: 0; | ||
line-height: 1; | ||
} | ||
:invalid { | ||
border-bottom-color: red; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<script lang="ts"> | ||
import { frac } from "$lib/frac" | ||
export let id: string | ||
export let value: number | ||
export let min: number | ||
export let max: number | ||
export let step: number | ||
export let required: boolean = false | ||
</script> | ||
|
||
<span class="range"> | ||
<output for={id}>{frac(value)}"</output> | ||
<input type="range" {id} bind:value {min} {max} {step} {required} /> | ||
</span> | ||
|
||
<style> | ||
.range { | ||
display: grid; | ||
grid-template-columns: 3rem 1fr; | ||
} | ||
input { | ||
width: 100%; | ||
box-sizing: border-box; | ||
background: inherit; | ||
color: inherit; | ||
border: none; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { describe, test, expect } from "vitest" | ||
import { frac, parseFrac } from "$lib/frac" | ||
|
||
describe("frac renders numbers as mixed fractions", () => { | ||
const cases: [number, string][] = [ | ||
[-1 - 3 / 8, "-1\u20093⁄8"], | ||
[-1, "-1"], | ||
[0, "0"], | ||
[1, "1"], | ||
[1.5, "1\u20091⁄2"], | ||
[12.25, "12\u20091⁄4"], | ||
[-6.125, "-6\u20091⁄8"], | ||
[1 / 4, "1⁄4"], | ||
[7 / 8, "7⁄8"], | ||
[15 / 16, "15⁄16"], | ||
[1 + 17 / 32, "1\u200917⁄32"], | ||
[23 / 32, "23⁄32"], | ||
[3 + 61.5 / 64, "3\u200931⁄32"], | ||
] | ||
test.for(cases)("renders %d as %s", ([n, s]) => { | ||
expect(frac(n)).toEqual(s) | ||
}) | ||
}) | ||
|
||
describe("parseFrac converts strings to numbers", () => { | ||
const cases: [string, number][] = [ | ||
// Whole numbers | ||
["1", 1], | ||
["1.", 1], | ||
["0", 0], | ||
["-1", -1], | ||
["-1.", -1], | ||
["-1.0", -1], | ||
// Decimal numbers | ||
["1.25", 1.25], | ||
["+1.25", 1.25], | ||
["+12.25", 12.25], | ||
// Vulgar fractions | ||
["1/64", 1 / 64], | ||
["5/32", 5 / 32], | ||
["-1/4", -0.25], | ||
["+6/32", 6 / 32], | ||
// Mixed fractions | ||
["1 3/4", 1.75], | ||
["-1 1/2", -1.5], | ||
// Basic expressions | ||
["6+1/2", 6.5], | ||
["4.5-4.5", 0], | ||
["4.0/2-2+3/5", 3 / 5], | ||
["8 1/2 1/4 1/8 -3/4", 8 + 7 / 8 - 3 / 4], | ||
["8 1/2 + 1/4 +1/8 - 3/4", 8 + 7 / 8 - 3 / 4], | ||
// Invalid input | ||
["", NaN], | ||
["abc", NaN], | ||
["12abc", NaN], | ||
["abc123", NaN], | ||
[" 123", NaN], | ||
["123 ", NaN], | ||
["1/2.3", NaN], | ||
] | ||
test.for(cases)("parses %j as %d", ([s, n]) => { | ||
expect(parseFrac(s)).toEqual(n) | ||
}) | ||
}) | ||
|
||
describe('parseFrac(frac(n)) round-trips within 1/32"', () => { | ||
test.for([1, 1.5, 23 / 32, 9000, -128 + 3 / 32])("%d", (n) => { | ||
const s = frac(n) | ||
expect(parseFrac(s)).toEqual(n) | ||
}) | ||
}) |
Oops, something went wrong.