mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-18 07:52:50 +00:00
168 lines
3.8 KiB
Svelte
168 lines
3.8 KiB
Svelte
<script>
|
|
import {layout} from "$lib/serial/connection.js"
|
|
import {KEYMAP_CODES} from "$lib/serial/keymap-codes.js"
|
|
|
|
export let activeLayer = 0
|
|
|
|
/** @type {{d: number, n: number, w: number, e: number, s: number}} */
|
|
export let keys
|
|
|
|
/** @type {'primary' | 'secondary' | 'tertiary'} */
|
|
export let type = "primary"
|
|
|
|
const layerNames = ["Primary Layer", "Number Layer", "Function Layer"]
|
|
|
|
const virtualLayerMap = [1, 0, 2]
|
|
const characterOffset = 8
|
|
|
|
function offsetDistance(quadrant, layer, activeLayer) {
|
|
const layerOffsetIndex = virtualLayerMap[layer] - virtualLayerMap[activeLayer]
|
|
const layerOffset = quadrant > 2 ? -characterOffset : characterOffset
|
|
return 25 * quadrant + layerOffsetIndex * layerOffset
|
|
}
|
|
|
|
/**
|
|
* @param keys {import('$lib/serial/keymap.js').KeyInfo[]}
|
|
* @returns {*}
|
|
*/
|
|
function getKeyDescriptions(keys) {
|
|
return keys.map(({title, id, code}, i) => `${title || id || code} (${layerNames[i]})`).join("\n")
|
|
}
|
|
|
|
/**
|
|
* @param id {number}
|
|
* @param layout {[number[], number[], number[]]}
|
|
* @returns import('$lib/serial/keymap.js').KeyInfo[]
|
|
*/
|
|
function getActions(id, layout) {
|
|
return Array.from({length: 3}).map((_, i) => {
|
|
const actionId = layout?.[i][id]
|
|
return actionId !== undefined ? KEYMAP_CODES[actionId] : {code: actionId}
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<div class="radial {type}">
|
|
{#each [keys.n, keys.e, keys.s, keys.w] as id, quadrant}
|
|
{@const actions = getActions(id, $layout)}
|
|
<button title={getKeyDescriptions(actions)}>
|
|
{#each actions as { symbol, id }, layer}
|
|
{#if symbol || id}
|
|
<span
|
|
class:active={virtualLayerMap[activeLayer] === virtualLayerMap[layer]}
|
|
style="offset-distance: {offsetDistance(quadrant, layer, activeLayer)}%">{symbol || id}</span
|
|
>
|
|
{/if}
|
|
{/each}
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
@use "sass:math";
|
|
|
|
$border-width: 18px;
|
|
$gap: 6px;
|
|
$size: 96;
|
|
$offset: 14;
|
|
$scale-difference: 0.2;
|
|
|
|
.radial {
|
|
position: relative;
|
|
|
|
container: radial / size;
|
|
|
|
width: #{$size * 1px};
|
|
height: #{$size * 1px};
|
|
|
|
transition: all 250ms ease;
|
|
}
|
|
|
|
span {
|
|
$cr: math.div($size, 2) - 2 * $offset;
|
|
|
|
will-change: scale, offset-distance;
|
|
user-select: none;
|
|
|
|
scale: 0.9;
|
|
offset-path: path(
|
|
"M#{math.div($size, 2)} #{$offset}A#{$cr} #{$cr} 0 1 1 #{math.div($size, 2)} #{$size - $offset}A#{$cr} #{$cr} 0 1 1 #{math.div($size, 2)} #{$offset}Z"
|
|
);
|
|
offset-rotate: 0deg;
|
|
|
|
display: flex;
|
|
grid-column: 1;
|
|
grid-row: 1;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
font-size: 16px;
|
|
|
|
opacity: 0.2;
|
|
|
|
transition: scale 250ms ease, opacity 250ms ease, offset-distance 250ms ease;
|
|
|
|
&.active {
|
|
scale: 1;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
button {
|
|
cursor: pointer;
|
|
|
|
position: absolute;
|
|
|
|
display: grid;
|
|
|
|
width: 100cqw;
|
|
height: 100cqh;
|
|
padding: 0;
|
|
|
|
font-family: "Noto Sans Mono", monospace;
|
|
font-size: 16px;
|
|
font-weight: 900;
|
|
color: var(--md-sys-color-on-surface-variant);
|
|
|
|
background: var(--md-sys-color-surface-variant);
|
|
border: none;
|
|
|
|
transition: all 250ms ease;
|
|
|
|
mask-image: url("$lib/assets/quater-ring.svg");
|
|
mask-size: 100% 100%;
|
|
|
|
&:active {
|
|
color: var(--md-sys-color-on-tertiary);
|
|
background: var(--md-sys-color-tertiary);
|
|
}
|
|
|
|
&:nth-child(1) {
|
|
clip-path: polygon(50% 50%, 0 0, 100% 0);
|
|
}
|
|
|
|
&:nth-child(2) {
|
|
clip-path: polygon(50% 50%, 100% 0, 100% 100%);
|
|
}
|
|
|
|
&:nth-child(3) {
|
|
clip-path: polygon(50% 50%, 0 100%, 100% 100%);
|
|
}
|
|
|
|
&:nth-child(4) {
|
|
clip-path: polygon(50% 50%, 0 0, 0 100%);
|
|
}
|
|
}
|
|
|
|
.secondary > button {
|
|
filter: brightness(80%) contrast(120%);
|
|
}
|
|
|
|
.tertiary > button {
|
|
filter: brightness(80%) contrast(110%);
|
|
}
|
|
</style>
|