refactor: use tailwind custom colors for themes
Use tailwind v4 @theme block so we can use bg-layer-0 instead of bg-[--layer-0] for theme colors.
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
interface Props {
|
||||
title?: string;
|
||||
transparent?: boolean;
|
||||
children?: import('svelte').Snippet;
|
||||
children?: Snippet;
|
||||
open?: boolean;
|
||||
}
|
||||
|
||||
let { title = 'Details', transparent = false, children, open = $bindable(false) }: Props =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<details class:transparent bind:open>
|
||||
<details class:transparent bind:open class="text-text outline-1 outline-outline bg-layer-1">
|
||||
<summary>{title}</summary>
|
||||
<div class="content">
|
||||
{@render children?.()}
|
||||
@@ -20,10 +20,7 @@
|
||||
<style>
|
||||
details {
|
||||
padding: 1em;
|
||||
color: var(--text-color);
|
||||
padding-left: 20px;
|
||||
background-color: #202020;
|
||||
outline: solid 0.1px white;
|
||||
border-radius: 2px;
|
||||
font-weight: 300;
|
||||
font-size: 0.9em;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { NodeInput } from '@nodarium/types';
|
||||
|
||||
import { Checkbox, Float, Select, Vec3 } from './index.js';
|
||||
import { InputCheckbox, InputNumber, InputSelect, InputVec3 } from './index';
|
||||
|
||||
interface Props {
|
||||
input: NodeInput;
|
||||
@@ -13,18 +13,18 @@
|
||||
</script>
|
||||
|
||||
{#if input.type === 'float'}
|
||||
<Float
|
||||
<InputNumber
|
||||
bind:value={value as number}
|
||||
min={input?.min}
|
||||
max={input?.max}
|
||||
step={input?.step}
|
||||
/>
|
||||
{:else if input.type === 'integer'}
|
||||
<Float bind:value={value as number} min={input?.min} max={input?.max} />
|
||||
<InputNumber bind:value={value as number} min={input?.min} max={input?.max} step={1} />
|
||||
{:else if input.type === 'boolean'}
|
||||
<Checkbox bind:value={value as boolean} {id} />
|
||||
<InputCheckbox bind:value={value as boolean} {id} />
|
||||
{:else if input.type === 'select'}
|
||||
<Select bind:value={value as number} options={input.options} {id} />
|
||||
<InputSelect bind:value={value as number} options={input.options} {id} />
|
||||
{:else if input.type === 'vec3'}
|
||||
<Vec3 bind:value={value as [number, number, number]} {id} />
|
||||
<InputVec3 bind:value={value as [number, number, number]} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
let { ctrl = false, shift = false, alt = false, key }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="command">
|
||||
<div class="command bg-layer-2">
|
||||
{#if ctrl}
|
||||
<span>Ctrl</span>
|
||||
{/if}
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
<style>
|
||||
.command {
|
||||
background: var(--layer-2);
|
||||
padding: 0.4em;
|
||||
padding-inline: 0.8em;
|
||||
font-size: 0.8em;
|
||||
border-radius: 0.3em;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "@iconify/tailwind4" {
|
||||
prefix: "i";
|
||||
}
|
||||
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}layer-0');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}layer-1');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}layer-2');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}layer-3');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}active');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}selected');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}outline{!,}');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}connection');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}edge');
|
||||
@source inline('{hover:,}{bg-,outline-,text-,}text');
|
||||
|
||||
/* fira-code-300 - latin */
|
||||
@font-face {
|
||||
@@ -58,9 +72,9 @@
|
||||
|
||||
--color-outline: var(--neutral-400);
|
||||
--color-connection: #333333;
|
||||
--color-edge: var(--connection, var(--outline));
|
||||
--color-edge: var(--connection, var(--color-outline));
|
||||
|
||||
--color-text-color: var(--neutral-200);
|
||||
--color-text: var(--neutral-200);
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -72,100 +86,89 @@ html {
|
||||
--neutral-800: #111111;
|
||||
--neutral-900: #060606;
|
||||
|
||||
--layer-0: var(--neutral-900);
|
||||
--layer-1: var(--neutral-500);
|
||||
--layer-2: var(--neutral-400);
|
||||
--layer-3: var(--neutral-200);
|
||||
--color-layer-0: var(--neutral-900);
|
||||
--color-layer-1: var(--neutral-500);
|
||||
--color-layer-2: var(--neutral-400);
|
||||
--color-layer-3: var(--neutral-200);
|
||||
|
||||
--active: #ffffff;
|
||||
--selected: #c65a19;
|
||||
--color-active: #ffffff;
|
||||
--color-selected: #c65a19;
|
||||
|
||||
--outline: var(--neutral-400);
|
||||
--connection: #333333;
|
||||
--edge: var(--connection, var(--outline));
|
||||
--color-outline: var(--neutral-400);
|
||||
--color-connection: #333333;
|
||||
--color-edge: var(--connection, var(--color-outline));
|
||||
|
||||
--text-color: var(--neutral-200);
|
||||
--color-text-color: var(--neutral-200);
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--text-color);
|
||||
background-color: var(--layer-0);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-layer-0);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body * {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
html.theme-light {
|
||||
--text-color: var(--neutral-800);
|
||||
--outline: var(--neutral-300);
|
||||
--layer-0: var(--neutral-100);
|
||||
--layer-1: var(--neutral-100);
|
||||
--layer-2: var(--neutral-200);
|
||||
--layer-3: var(--neutral-500);
|
||||
--active: #000000;
|
||||
--selected: #c65a19;
|
||||
--connection: #888;
|
||||
--color-text: var(--neutral-800);
|
||||
--color-outline: var(--neutral-300);
|
||||
--color-layer-0: var(--neutral-100);
|
||||
--color-layer-1: var(--neutral-100);
|
||||
--color-layer-2: var(--neutral-200);
|
||||
--color-layer-3: var(--neutral-500);
|
||||
--color-active: #000000;
|
||||
--color-selected: #c65a19;
|
||||
--color-connection: #888;
|
||||
}
|
||||
|
||||
html.theme-solarized {
|
||||
--text-color: #425055;
|
||||
--outline: #93a1a1;
|
||||
--layer-0: #fdf6e3;
|
||||
--layer-1: #eee8d5;
|
||||
--layer-2: #c4c0b4;
|
||||
--layer-3: #586e75;
|
||||
--active: #000000;
|
||||
--selected: #268bd2;
|
||||
--connection: #839496;
|
||||
--color-text: #425055;
|
||||
--color-outline: #93a1a1;
|
||||
--color-layer-0: #fdf6e3;
|
||||
--color-layer-1: #eee8d5;
|
||||
--color-layer-2: #c4c0b4;
|
||||
--color-layer-3: #586e75;
|
||||
--color-active: #000000;
|
||||
--color-selected: #268bd2;
|
||||
--color-connection: #839496;
|
||||
}
|
||||
|
||||
html.theme-catppuccin {
|
||||
--text-color: #cdd6f4;
|
||||
--outline: #3e3e4f;
|
||||
--layer-3: #a8aac8;
|
||||
--layer-2: #313244;
|
||||
--layer-1: #1e1e2e;
|
||||
--layer-0: #11111b;
|
||||
--connection: #444459;
|
||||
--color-text: #cdd6f4;
|
||||
--color-outline: #3e3e4f;
|
||||
--color-layer-3: #a8aac8;
|
||||
--color-layer-2: #313244;
|
||||
--color-layer-1: #1e1e2e;
|
||||
--color-layer-0: #11111b;
|
||||
--color-connection: #444459;
|
||||
}
|
||||
|
||||
html.theme-high-contrast {
|
||||
--text-color: #ffffff;
|
||||
--outline: white;
|
||||
--layer-0: #000000;
|
||||
--layer-1: black;
|
||||
--layer-2: #222222;
|
||||
--layer-3: #ffffff;
|
||||
--connection: #fff;
|
||||
--color-text: #ffffff;
|
||||
--color-outline: white;
|
||||
--color-layer-0: #000000;
|
||||
--color-layer-1: black;
|
||||
--color-layer-2: #222222;
|
||||
--color-layer-3: #ffffff;
|
||||
--color-connection: #fff;
|
||||
}
|
||||
|
||||
html.theme-nord {
|
||||
--text-color: #d8dee9;
|
||||
--outline: #4c566a;
|
||||
--layer-0: #2e3440;
|
||||
--layer-1: #3b4252;
|
||||
--layer-2: #434c5e;
|
||||
--layer-3: #5e81ac;
|
||||
--active: #8999bd;
|
||||
--selected: #b76c3f;
|
||||
--connection: #4c566a;
|
||||
--color-text: #d8dee9;
|
||||
--color-outline: #4c566a;
|
||||
--color-layer-0: #2e3440;
|
||||
--color-layer-1: #3b4252;
|
||||
--color-layer-2: #434c5e;
|
||||
--color-layer-3: #5e81ac;
|
||||
--color-active: #8999bd;
|
||||
--color-selected: #b76c3f;
|
||||
--color-connection: #4c566a;
|
||||
}
|
||||
|
||||
html.theme-dracula {
|
||||
--text-color: #f8f8f2;
|
||||
--outline: #6272a4;
|
||||
--layer-0: #282a36;
|
||||
--layer-1: #44475a;
|
||||
--layer-2: #32374d;
|
||||
--layer-3: #bd93f9;
|
||||
--connection: #6272a4;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: var(--layer-2);
|
||||
border: 1px solid var(--outline);
|
||||
padding: 8px 9px;
|
||||
border-radius: 4px;
|
||||
--color-text: #f8f8f2;
|
||||
--color-outline: #6272a4;
|
||||
--color-layer-0: #282a36;
|
||||
--color-layer-1: #44475a;
|
||||
--color-layer-2: #32374d;
|
||||
--color-layer-3: #bd93f9;
|
||||
--color-connection: #6272a4;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export { default as Input } from './Input.svelte';
|
||||
export { default as Checkbox } from './inputs/Checkbox.svelte';
|
||||
export { default as Float } from './inputs/Float.svelte';
|
||||
export { default as Select } from './inputs/Select.svelte';
|
||||
export { default as Vec3 } from './inputs/Vec3.svelte';
|
||||
export { default as InputCheckbox } from './inputs/InputCheckbox.svelte';
|
||||
export { default as InputNumber } from './inputs/InputNumber.svelte';
|
||||
export { default as InputSelect } from './inputs/InputSelect.svelte';
|
||||
export { default as InputVec3 } from './inputs/InputVec3.svelte';
|
||||
|
||||
export { default as Details } from './Details.svelte';
|
||||
export { default as ShortCut } from './ShortCut.svelte';
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
value?: number;
|
||||
step?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
value = $bindable(0.5),
|
||||
step,
|
||||
min = $bindable(0),
|
||||
max = $bindable(1),
|
||||
id
|
||||
}: Props = $props();
|
||||
|
||||
if (min > max) {
|
||||
[min, max] = [max, min];
|
||||
}
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
|
||||
const precision = $derived(
|
||||
((step || value).toString().split('.')[1] || '').length
|
||||
);
|
||||
|
||||
function strip(input: number) {
|
||||
return +parseFloat(input + '').toFixed(precision);
|
||||
}
|
||||
|
||||
let inputEl: HTMLInputElement | undefined = $state();
|
||||
|
||||
let oldValue: number;
|
||||
function handleChange() {
|
||||
if (value === oldValue) return;
|
||||
oldValue = value;
|
||||
}
|
||||
|
||||
let isMouseDown = $state(false);
|
||||
let downV = 0;
|
||||
let vx = 0;
|
||||
let rect: DOMRect;
|
||||
|
||||
function handleMouseDown(ev: MouseEvent) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!inputEl) return;
|
||||
|
||||
inputEl.focus();
|
||||
|
||||
isMouseDown = true;
|
||||
|
||||
downV = value;
|
||||
rect = inputEl.getBoundingClientRect();
|
||||
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
window.addEventListener('mouseup', handleMouseUp);
|
||||
document.body.style.cursor = 'ew-resize';
|
||||
(ev.target as HTMLElement)?.blur();
|
||||
}
|
||||
|
||||
function handleMouseUp() {
|
||||
isMouseDown = false;
|
||||
|
||||
if (downV === value) {
|
||||
inputEl?.focus();
|
||||
}
|
||||
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
|
||||
if (value < min) {
|
||||
min = value;
|
||||
}
|
||||
|
||||
document.body.style.cursor = 'unset';
|
||||
window.removeEventListener('mouseup', handleMouseUp);
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
}
|
||||
|
||||
function handleKeyDown(ev: KeyboardEvent) {
|
||||
if (ev.key === 'Escape' || ev.key === 'Enter') {
|
||||
handleMouseUp();
|
||||
inputEl?.blur();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(ev: MouseEvent) {
|
||||
vx = (ev.clientX - rect.left) / rect.width;
|
||||
|
||||
if (ev.ctrlKey) {
|
||||
let v = min + (max - min) * vx;
|
||||
value = v;
|
||||
} else {
|
||||
value = Math.max(Math.min(min + (max - min) * vx, max), min);
|
||||
}
|
||||
|
||||
value = strip(value);
|
||||
|
||||
// With ctrl + outside of input ev.target becomes HTMLDocument
|
||||
if (ev.target instanceof HTMLElement) {
|
||||
ev.target?.blur();
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (value.toString().length > 5) {
|
||||
value = strip(value);
|
||||
}
|
||||
if (value !== undefined) {
|
||||
handleChange();
|
||||
}
|
||||
});
|
||||
|
||||
let width = $derived(
|
||||
Number.isFinite(value)
|
||||
? Math.max((value?.toString().length ?? 1) * 8, 50) + 'px'
|
||||
: '20px'
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="component-wrapper" class:is-down={isMouseDown}>
|
||||
<span class="overlay" style={`width: ${((value - min) / (max - min)) * 100}%`}></span>
|
||||
<input
|
||||
bind:value
|
||||
bind:this={inputEl}
|
||||
{id}
|
||||
{step}
|
||||
{max}
|
||||
{min}
|
||||
onkeydown={handleKeyDown}
|
||||
onmousedown={handleMouseDown}
|
||||
onmouseup={handleMouseUp}
|
||||
type="number"
|
||||
style={`width:${width};`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.component-wrapper {
|
||||
position: relative;
|
||||
background-color: var(--layer-2, #4b4b4b);
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
transition: box-shadow 0.3s ease;
|
||||
border: solid 1px var(--outline);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius, 2px);
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
font-family: var(--font-family);
|
||||
font-variant-numeric: tabular-nums;
|
||||
cursor: pointer;
|
||||
color: var(--text-color);
|
||||
background-color: transparent;
|
||||
padding: var(--padding, 6px);
|
||||
font-size: 1em;
|
||||
padding-inline: 10px;
|
||||
text-align: center;
|
||||
border: none;
|
||||
border-style: none;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.is-down > input {
|
||||
cursor: ew-resize !important;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
background-color: var(--text-color);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.is-down > .overlay {
|
||||
transition: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -18,7 +18,7 @@
|
||||
</script>
|
||||
|
||||
<label
|
||||
class="relative inline-flex h-[22px] w-[22px] cursor-pointer items-center justify-center bg-[var(--layer-2)] rounded-[5px]"
|
||||
class="relative inline-flex h-5.5 w-5.5 cursor-pointer items-center justify-center bg-layer-2 rounded-[5px]"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -33,7 +33,7 @@
|
||||
viewBox="0 0 19 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-[10px] w-[12px] text-[var(--text-color)]"
|
||||
class="h-2.5 w-3 text-text"
|
||||
>
|
||||
<path
|
||||
d="M2 7L7 12L17 2"
|
||||
174
packages/ui/src/lib/inputs/InputNumber.svelte
Normal file
174
packages/ui/src/lib/inputs/InputNumber.svelte
Normal file
@@ -0,0 +1,174 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
value?: number;
|
||||
step?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
value = $bindable(0.5),
|
||||
step,
|
||||
min = $bindable(0),
|
||||
max = $bindable(1),
|
||||
id
|
||||
}: Props = $props();
|
||||
|
||||
// normalize bounds
|
||||
if (min > max) [min, max] = [max, min];
|
||||
if (value > max) max = value;
|
||||
|
||||
let inputEl: HTMLInputElement | undefined = $state();
|
||||
|
||||
function clamp(v: number) {
|
||||
return Math.min(max, Math.max(min, v));
|
||||
}
|
||||
|
||||
function snap(v: number) {
|
||||
if (step) v = Math.round(v / step) * step;
|
||||
return +v.toFixed(3);
|
||||
}
|
||||
|
||||
let dragging = $state(false);
|
||||
let startValue = 0;
|
||||
let rect: DOMRect;
|
||||
|
||||
function onMouseDown(e: MouseEvent) {
|
||||
if (!inputEl) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
dragging = true;
|
||||
startValue = value;
|
||||
rect = inputEl.getBoundingClientRect();
|
||||
|
||||
document.body.style.cursor = 'ew-resize';
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
}
|
||||
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
const ratio = (e.clientX - rect.left) / rect.width;
|
||||
let delta = (min + (max - min) * ratio) - startValue;
|
||||
|
||||
if (e.shiftKey) delta /= 5;
|
||||
|
||||
value = snap(
|
||||
e.ctrlKey
|
||||
? startValue + delta
|
||||
: clamp(startValue + delta)
|
||||
);
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
dragging = false;
|
||||
document.body.style.cursor = '';
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
window.removeEventListener('mouseup', onMouseUp);
|
||||
if (startValue === value) {
|
||||
inputEl?.focus();
|
||||
}
|
||||
|
||||
min = Math.min(min, value);
|
||||
max = Math.max(max, value);
|
||||
}
|
||||
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' || e.key === 'Enter') {
|
||||
onMouseUp();
|
||||
inputEl?.blur();
|
||||
}
|
||||
}
|
||||
|
||||
function windowKeyDown(e: KeyboardEvent) {
|
||||
if (!dragging) return;
|
||||
if (e.shiftKey) {
|
||||
startValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
const width = $derived(
|
||||
Number.isFinite(value)
|
||||
? `${Math.max(value.toString().length * 8, 50)}px`
|
||||
: '20px'
|
||||
);
|
||||
|
||||
function stepUp(e: MouseEvent) {
|
||||
value = snap(value + (e.shiftKey ? step! * 10 : step!));
|
||||
}
|
||||
|
||||
function stepDown(e: MouseEvent) {
|
||||
value = snap(value - (e.shiftKey ? step! * 10 : step!));
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
value = snap(clamp(value));
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={windowKeyDown} />
|
||||
|
||||
<div
|
||||
class="component-wrapper relative flex items-stretch overflow-hidden rounded-sm border border-outline bg-layer-2 select-none transition-shadow"
|
||||
class:cursor-ew-resize={dragging}
|
||||
>
|
||||
{#if step}
|
||||
<button
|
||||
aria-label="step down"
|
||||
onmousedown={stepDown}
|
||||
class="cursor-pointer w-4 bg-layer-3 opacity-30 hover:opacity-50"
|
||||
>
|
||||
<span class="i-[tabler--chevron-compact-left] block h-full w-full text-outline!"></span>
|
||||
</button>
|
||||
<div class="w-px bg-outline"></div>
|
||||
{/if}
|
||||
|
||||
<div class="relative grow">
|
||||
<div
|
||||
class="absolute inset-y-0 left-0 bg-layer-3 opacity-30 pointer-events-none transition-[width]"
|
||||
class:transition-none={dragging}
|
||||
style={`width: ${Math.min((value - min) / (max - min), 1) * 100}%`}
|
||||
>
|
||||
</div>
|
||||
|
||||
<input
|
||||
bind:this={inputEl}
|
||||
bind:value
|
||||
{id}
|
||||
{step}
|
||||
{min}
|
||||
{max}
|
||||
type="number"
|
||||
onkeydown={onKeyDown}
|
||||
onmousedown={onMouseDown}
|
||||
class="w-full min-w-full cursor-pointer bg-transparent px-2 py-1 text-center font-tabular text-text outline-none appearance-none"
|
||||
style:width={width}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if step}
|
||||
<div class="w-px bg-outline"></div>
|
||||
<button
|
||||
aria-label="step up"
|
||||
onmousedown={stepUp}
|
||||
class="cursor-pointer w-4 bg-layer-3 opacity-30 hover:opacity-50"
|
||||
>
|
||||
<span class="i-[tabler--chevron-compact-right] block h-full w-full text-outline!"></span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
input[type="number"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@
|
||||
let { options = [], value = $bindable(0), id = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<select {id} bind:value>
|
||||
<select {id} bind:value class="bg-layer-2 text-text">
|
||||
{#each options as label, i (label)}
|
||||
<option value={i}>{label}</option>
|
||||
{/each}
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
<style>
|
||||
select {
|
||||
background: var(--layer-2);
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-family);
|
||||
outline: solid 1px var(--outline);
|
||||
outline: solid 1px var(--color-outline);
|
||||
padding: 0.8em 1em;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Float from './Float.svelte';
|
||||
import InputNumber from './InputNumber.svelte';
|
||||
|
||||
interface Props {
|
||||
value?: number[];
|
||||
@@ -10,9 +10,9 @@
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Float id={`${id}-x`} bind:value={value[0]} step={0.01} />
|
||||
<Float id={`${id}-y`} bind:value={value[1]} step={0.01} />
|
||||
<Float id={`${id}-z`} bind:value={value[2]} step={0.01} />
|
||||
<InputNumber id={`${id}-x`} bind:value={value[0]} step={0.01} />
|
||||
<InputNumber id={`${id}-y`} bind:value={value[1]} step={0.01} />
|
||||
<InputNumber id={`${id}-z`} bind:value={value[2]} step={0.01} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -23,9 +23,9 @@
|
||||
div > :global(.component-wrapper:nth-child(2)) {
|
||||
border-radius: 0px !important;
|
||||
outline: none;
|
||||
border: solid thin var(--outline);
|
||||
border-top: solid thin color-mix(in srgb, var(--outline) 50%, transparent);
|
||||
border-bottom: solid thin color-mix(in srgb, var(--outline) 50%, transparent);
|
||||
border: solid thin var(--color-outline);
|
||||
border-top: solid 1px var(--color-outline);
|
||||
border-bottom: solid 1px var(--color-outline);
|
||||
}
|
||||
div > :global(.component-wrapper:nth-child(3)) {
|
||||
border-top: none !important;
|
||||
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import '$lib/app.css';
|
||||
import { Checkbox, Details, Float, Select, ShortCut, Vec3 } from '$lib/index.js';
|
||||
import { Details, InputCheckbox, InputNumber, InputSelect, InputVec3, ShortCut } from '$lib';
|
||||
import Section from './Section.svelte';
|
||||
|
||||
let intValue = $state(0);
|
||||
let floatValue = $state(0.2);
|
||||
let float2Value = $state(0.02);
|
||||
let float3Value = $state(1);
|
||||
let vecValue = $state([0.2, 0.3, 0.4]);
|
||||
const options = ['strawberry', 'raspberry', 'chickpeas'];
|
||||
@@ -16,6 +15,7 @@
|
||||
let detailsOpen = $state(false);
|
||||
|
||||
const themes = [
|
||||
'dark',
|
||||
'light',
|
||||
'solarized',
|
||||
'catppuccin',
|
||||
@@ -31,40 +31,63 @@
|
||||
}
|
||||
document.documentElement.classList.add(`theme-${themes[themeIndex]}`);
|
||||
});
|
||||
|
||||
const colors = [
|
||||
'layer-0',
|
||||
'layer-1',
|
||||
'layer-2',
|
||||
'layer-3',
|
||||
'active',
|
||||
'selected',
|
||||
'outline',
|
||||
'connection',
|
||||
'edge',
|
||||
'text'
|
||||
];
|
||||
</script>
|
||||
|
||||
<main class="flex flex-col gap-8 py-8">
|
||||
<div class="flex gap-4">
|
||||
<h1 class="text-4xl">@nodarium/ui</h1>
|
||||
<Select bind:value={themeIndex} options={themes}></Select>
|
||||
<InputSelect bind:value={themeIndex} options={themes}></InputSelect>
|
||||
</div>
|
||||
|
||||
<Section title="Integer (step inherit)" value={intValue}>
|
||||
<Float bind:value={intValue} max={2} />
|
||||
<Section title="Colors">
|
||||
<table>
|
||||
<tbody>
|
||||
{#each colors as color (color)}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="w-6 h-6 mr-2 my-1 rounded-sm outline-1 bg-{color}"></div>
|
||||
</td>
|
||||
<td>{color}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<Section title="Float (step inherit)" value={floatValue}>
|
||||
<Float bind:value={floatValue} />
|
||||
</Section>
|
||||
|
||||
<Section title="Float 2 (step inherit)" value={intValue}>
|
||||
<Float bind:value={float2Value} />
|
||||
</Section>
|
||||
|
||||
<Section title="Float (0.01 step)" value={floatValue}>
|
||||
<Float bind:value={float3Value} step={0.01} max={3} />
|
||||
<Section title="InputNumber">
|
||||
<div class="flex flex-col gap-2">
|
||||
<p>value={floatValue}</p>
|
||||
<InputNumber bind:value={floatValue} />
|
||||
<p>min=0 max=10 step=1 value={intValue}</p>
|
||||
<InputNumber bind:value={intValue} min={0} max={10} step={1} />
|
||||
<p>min=0 max=3 step=0.01 value={float3Value}</p>
|
||||
<InputNumber bind:value={float3Value} step={0.01} max={3} />
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="Vec3" value={JSON.stringify(vecValue)}>
|
||||
<Vec3 bind:value={vecValue} />
|
||||
<InputVec3 bind:value={vecValue} />
|
||||
</Section>
|
||||
|
||||
<Section title="Select" value={d}>
|
||||
<Select bind:value={selectValue} {options} />
|
||||
<InputSelect bind:value={selectValue} {options} />
|
||||
</Section>
|
||||
|
||||
<Section title="Checkbox" value={checked}>
|
||||
<Checkbox bind:value={checked} />
|
||||
<InputCheckbox bind:value={checked} />
|
||||
</Section>
|
||||
|
||||
<Section title="Details" value={detailsOpen}>
|
||||
@@ -74,7 +97,11 @@
|
||||
</Section>
|
||||
|
||||
<Section title="Shortcut">
|
||||
<ShortCut ctrl key="S" />
|
||||
<div class="flex gap-4">
|
||||
<ShortCut ctrl key="S" />
|
||||
<ShortCut alt key="F4" />
|
||||
<ShortCut alt ctrl key="delete" />
|
||||
</div>
|
||||
</Section>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { type Snippet } from 'svelte';
|
||||
let { title, value, children } = $props<{
|
||||
let { title, value, children, class: _class } = $props<{
|
||||
title?: string;
|
||||
value?: unknown;
|
||||
children?: Snippet;
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<section class="border border-1/2 mb-4 p-4 flex flex-col gap-4">
|
||||
<section class="border-outline border-1/2 bg-layer-1 rounded border mb-4 p-4 flex flex-col gap-4 {_class}">
|
||||
<h3 class="flex gap-2 font-bold">
|
||||
{title}
|
||||
<p class="font-normal! opacity-50!">{value}</p>
|
||||
|
||||
Reference in New Issue
Block a user