move error calc, add preview syntax

This commit is contained in:
faiztyanirh 2026-03-10 13:04:27 +07:00
parent 05ee4617d5
commit 45c8d6969a
2 changed files with 228 additions and 325 deletions

View File

@ -18,9 +18,6 @@
let cursorIndex = $state(0); let cursorIndex = $state(0);
let showLiteralPopover = $state(false); let showLiteralPopover = $state(false);
let literalPopoverType = $state(('string')); let literalPopoverType = $state(('string'));
$inspect(tokens)
// let expression = $state('');
// let cursorPosition = $state(0);
function uid() { function uid() {
return Math.random().toString(36).slice(2, 9); return Math.random().toString(36).slice(2, 9);
@ -106,92 +103,6 @@ $inspect(tokens)
props.calFormState.form[key] = []; props.calFormState.form[key] = [];
props.calFormState.validateField?.(key, [], false); props.calFormState.validateField?.(key, [], false);
} }
// function unselectAll(key) {
// props.calFormState.form[key] = [];
// props.calFormState.validateField?.(key, [], false);
// }
// function getErrorStatus(formulaCode = '') {
// const selected = props.calFormState.form.FormulaInput;
// if (!Array.isArray(selected)) return [];
// return selected.map((item) => ({
// value: item.value,
// done: new RegExp(`\\b${item.value}\\b`, 'i').test(formulaCode)
// }));
// }
// function addToExpression(text) {
// const before = expression.slice(0, cursorPosition);
// const after = expression.slice(cursorPosition);
// expression = before + text + after;
// cursorPosition += text.length;
// }
// function addOperator(op) {
// addToExpression(op);
// props.calFormState.form.FormulaCode = expression;
// props.calFormState.validateField?.('FormulaCode', expression, false);
// }
// function addValue(val) {
// addToExpression(val);
// props.calFormState.form.FormulaCode = expression;
// props.calFormState.validateField?.('FormulaCode', expression, false);
// }
// function handleInput(e) {
// expression = e.target.value;
// cursorPosition = e.target.selectionStart;
// formState.form.FormulaCode = expression;
// formState.validateField?.('FormulaCode', expression, false);
// }
// function handleClick(e) {
// cursorPosition = e.target.selectionStart;
// }
// function handleContainerClick(e) {
// const rect = e.currentTarget.getBoundingClientRect();
// const text = expression;
// const charWidth = 8.5;
// const padding = 12;
// const clickX = e.clientX - rect.left - padding;
// let newPos = Math.floor(clickX / charWidth);
// newPos = Math.max(0, Math.min(newPos, text.length));
// cursorPosition = newPos;
// }
// function moveCursorLeft() {
// if (cursorPosition > 0) {
// cursorPosition -= 1;
// }
// }
// function moveCursorRight() {
// if (cursorPosition < expression.length) {
// cursorPosition += 1;
// }
// }
// function deleteChar() {
// if (cursorPosition > 0) {
// const before = expression.slice(0, cursorPosition - 1);
// const after = expression.slice(cursorPosition);
// expression = before + after;
// cursorPosition -= 1;
// props.calFormState.form.FormulaCode = expression;
// props.calFormState.validateField?.('FormulaCode', expression, false);
// }
// }
// function clearExpression() {
// expression = '';
// cursorPosition = 0;
// props.calFormState.form.FormulaCode = expression;
// props.calFormState.validateField?.('FormulaCode', expression, false);
// }
</script> </script>
<div class="flex flex-col gap-4 w-full"> <div class="flex flex-col gap-4 w-full">

View File

@ -126,6 +126,20 @@
{#if required} {#if required}
<span class="text-destructive text-xl leading-none h-3.5">*</span> <span class="text-destructive text-xl leading-none h-3.5">*</span>
{/if} {/if}
{#if key === 'FormulaCode' && formState.form.FormulaInput?.length}
{@const inputStatus = onGetErrorStatus?.()}
<div class="flex items-center gap-2 text-sm text-destructive">
<span>Must included :</span>
<div class="flex gap-1 flex-wrap">
{#each inputStatus as item (item.value)}
<Badge class="px-1 text-[10px]" variant={item.done ? 'default' : 'destructive'}>
{item.value}
</Badge>
{/each}
</div>
</div>
{/if}
</div> </div>
<div class="relative flex flex-col items-center w-full"> <div class="relative flex flex-col items-center w-full">
@ -497,9 +511,8 @@
}} }}
> >
{#if tokens.length === 0} {#if tokens.length === 0}
<span class="text-muted-foreground text-xs italic">Click buttons below to build formula...</span> <span class="text-muted-foreground text-xs italic">Select test then click buttons below to build formula</span>
{:else} {:else}
<!-- Split tokens into lines -->
{@const lines = (() => { {@const lines = (() => {
const result = [[]]; const result = [[]];
tokens.forEach((tok, idx) => { tokens.forEach((tok, idx) => {
@ -516,21 +529,19 @@
{/if} {/if}
<button <button
type="button" type="button"
class="inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold border transition-colors " class="inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold border transition-colors"
onclick={() => onSetCursor(idx + 1)} onclick={() => onSetCursor(idx + 1)}
title="Click to move cursor here"
> >
{tok.value} {tok.value}
</button> </button>
{/each} {/each}
{#if line.length > 0 && cursorIndex === line[line.length - 1].idx + 1} {#if line.length > 0 && cursorIndex === line[line.length - 1].idx + 1}
<span class="animate-cursor inline-block h-5 w-0.5 bg-primary rounded"></span> <span class="animate-cursor inline-block h-5 w-0.5 bg-primary rounded"></span>
{:else if line.length === 0 && lineIdx === lines.length - 1}
<span class="animate-cursor inline-block h-5 w-0.5 bg-primary rounded"></span>
{/if} {/if}
</div> </div>
{/each} {/each}
{#if cursorIndex === tokens.length && (tokens.length === 0 || tokens[tokens.length - 1].type === 'newline')}
<span class="animate-cursor inline-block h-5 w-0.5 bg-primary rounded mt-1"></span>
{/if}
{/if} {/if}
</div> </div>
<Button type="button" variant="outline" size="icon" onclick={onMoveCursorRight}> <Button type="button" variant="outline" size="icon" onclick={onMoveCursorRight}>
@ -548,9 +559,8 @@
</div> </div>
{#if formState.form.FormulaInput?.length > 0} {#if formState.form.FormulaInput?.length > 0}
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<!-- Selected Tests -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm font-medium">Selected Tests</span> <Label class="uppercase tracking-wide">Selected Tests</Label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{#each formState.form.FormulaInput as item (item)} {#each formState.form.FormulaInput as item (item)}
<Button <Button
@ -567,9 +577,8 @@
<!-- Custom Literal Values --> <!-- Custom Literal Values -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm font-medium">Custom Values</span> <Label class="uppercase tracking-wide">Custom Values</Label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<!-- String literal popover -->
<Popover.Root bind:open={stringPopoverOpen}> <Popover.Root bind:open={stringPopoverOpen}>
<Popover.Trigger> <Popover.Trigger>
{#snippet child({ props: triggerProps })} {#snippet child({ props: triggerProps })}
@ -616,7 +625,6 @@
</Popover.Content> </Popover.Content>
</Popover.Root> </Popover.Root>
<!-- Number literal popover -->
<Popover.Root bind:open={numberPopoverOpen}> <Popover.Root bind:open={numberPopoverOpen}>
<Popover.Trigger> <Popover.Trigger>
{#snippet child({ props: triggerProps })} {#snippet child({ props: triggerProps })}
@ -666,9 +674,8 @@
</div> </div>
</div> </div>
<!-- Logical Operators (Keywords) -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm font-medium">Logical Operators</span> <Label class="uppercase tracking-wide">Logical Operators</Label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{#each logicalop as op} {#each logicalop as op}
<Button <Button
@ -685,7 +692,7 @@
<!-- Comparison Operators --> <!-- Comparison Operators -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm font-medium">Comparison Operators</span> <Label class="uppercase tracking-wide">Comparison Operators</Label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{#each comparisonop as op} {#each comparisonop as op}
<Button <Button
@ -702,7 +709,7 @@
<!-- Math Operators --> <!-- Math Operators -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm font-medium">Math Operators</span> <Label class="uppercase tracking-wide">Math Operators</Label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{#each operators as op} {#each operators as op}
<Button <Button
@ -717,11 +724,12 @@
</div> </div>
</div> </div>
<!-- Preview -->
{#if tokens.length > 0} {#if tokens.length > 0}
<div class="section"> <div class="flex flex-col gap-2">
<label class="section-label">Preview</label> <Label class="uppercase tracking-wide">Preview</Label>
<pre class="expression-preview">{expressionString}</pre> <div class="border-2 border-dashed border-muted-foreground/30 rounded-lg">
<pre class="font-mono text-sm bg-muted/50 p-2 rounded">{expressionString}</pre>
</div>
</div> </div>
{/if} {/if}
</div> </div>
@ -814,22 +822,6 @@
{errorMessage} {errorMessage}
</div> </div>
{/if} {/if}
{#if key === 'FormulaCode' && formState.form.FormulaInput?.length}
{@const inputStatus = onGetErrorStatus?.()}
<div class="flex items-center gap-2 text-sm text-destructive">
<span>Must included :</span>
<div class="flex gap-1 flex-wrap">
{#each inputStatus as item (item.value)}
<Badge variant={item.done ? 'default' : 'destructive'}>
{item.value}
</Badge>
{/each}
</div>
</div>
{/if}
</div> </div>
</div> </div>
</div> </div>