+
+
+
+
+
diff --git a/src/lib/components/reusable/form/dictionary-form-renderer.svelte b/src/lib/components/reusable/form/dictionary-form-renderer.svelte
index b8c99f3..cf610f8 100644
--- a/src/lib/components/reusable/form/dictionary-form-renderer.svelte
+++ b/src/lib/components/reusable/form/dictionary-form-renderer.svelte
@@ -11,6 +11,10 @@
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
import { Textarea } from "$lib/components/ui/textarea/index.js";
+ import MoveLeftIcon from "@lucide/svelte/icons/move-left";
+ import MoveRightIcon from "@lucide/svelte/icons/move-right";
+ import BrushCleaningIcon from "@lucide/svelte/icons/brush-cleaning";
+ import DeleteIcon from "@lucide/svelte/icons/delete";
let {
formState,
@@ -26,8 +30,12 @@
handleRefTypeChange,
} = $props();
+ const operators = ['+', '-', '*', '/', '^', '(', ')'];
+
let searchQuery = $state({});
let dropdownOpen = $state({});
+ let expression = $state('');
+ let cursorPosition = $state(0);
function getFilteredOptions(key) {
const query = searchQuery[key] || "";
@@ -67,6 +75,94 @@
formState.form[field.key] = field.defaultValue;
}
}
+
+ function unselectAll(key) {
+ formState.form[key] = [];
+ formState.validateField?.(key, [], false);
+ }
+
+ // function getErrorStatus(key) {
+ // const error = formState.errors[key];
+ // if (!error) return [];
+
+ // const parts = error.split(':');
+ // if (parts.length < 2) return [];
+
+ // const values = parts[1].split(',').map(v => v.trim());
+ // return values.map(v => ({ value: v, done: false }));
+ // }
+ $inspect(formState.form.FormulaInput)
+
+ function getErrorStatus(formulaCode = '') {
+ const selected = formState.form.FormulaInput;
+ if (!Array.isArray(selected)) return [];
+
+ return selected.map(v => ({
+ value: v,
+ done: formulaCode.includes(v)
+ }));
+ }
+
+ function addToExpression(text) {
+ console.log(text);
+ const before = expression.slice(0, cursorPosition);
+ const after = expression.slice(cursorPosition);
+ expression = before + text + after;
+ cursorPosition += text.length;
+ }
+
+ function addOperator(op) {
+ addToExpression(op);
+ }
+
+ function addValue(val) {
+ addToExpression(val);
+ }
+
+ function handleInput(e) {
+ expression = e.target.value;
+ cursorPosition = e.target.selectionStart;
+ }
+
+ 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;
+ }
+ }
+
+ function clearExpression() {
+ expression = '';
+ cursorPosition = 0;
+ }
{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, validateOn, dependsOn, endpointParamKey, valueKey, labelKey, txtKey })}
@@ -234,6 +330,68 @@
{/if}
+ {:else if type === "selectmultiple"}
+ {@const filteredOptions = getFilteredOptions(key)}
+
{
+ formState.form[key] = val;
+ if (validateOn?.includes("input")) {
+ formState.validateField?.(key, val, false);
+ formState.validateField?.("FormulaCode", val, false);
+ }
+ }}
+ onOpenChange={(open) => {
+ if (open && optionsEndpoint) {
+ formState.fetchOptions?.(
+ { key, optionsEndpoint, dependsOn, endpointParamKey, valueKey, labelKey },
+ formState.form
+ );
+ }
+ }}
+ >
+
+ {formState.form[key]?.length
+ ? (formState.selectOptions?.[key] ?? [])
+ .filter(o => formState.form[key].includes(o.value))
+ .map(o => o.label)
+ .join(', ')
+ : 'Choose'}
+
+
+
+
+
+
+ {#if formState.loadingOptions?.[key]}
+
+ Loading...
+
+ {:else}
+ {#if formState.form[key].length > 0}
+
+ unselectAll(key)}
+ >
+ Unselect All
+
+ {/if}
+ {#each filteredOptions as opt (opt.value)}
+
+ {opt.label}
+
+ {/each}
+ {/if}
+
+
+
{:else if type === "date"}
+ {:else if type === "formulabuilder"}
+
+
Formula parameters:
+
+
+ {#each inputStatus as item (item.value)}
+
+ {item.value}
+
+ {/each}
+
+
{/if}
@@ -428,4 +693,20 @@
{/each}
{/each}
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/reusable/form/test.js b/src/lib/components/reusable/form/test.js
new file mode 100644
index 0000000..cb4afaf
--- /dev/null
+++ b/src/lib/components/reusable/form/test.js
@@ -0,0 +1,290 @@
+
+
+
+
+
+ {selectedLabel}
+
+
+
+ Fruits
+ {#each fruits as fruit (fruit.value)}
+
+ {fruit.label}
+
+ {/each}
+
+ {#if selectedValues.length > 0}
+
+
+ Unselect All
+
+ {/if}
+
+
+
+ {#if selectedValues.length > 0}
+
+
Selected Values
+
+ {#each selectedValues as item (item)}
+ addValue(item)}
+ >
+ {item}
+
+ {/each}
+
+
+ {/if}
+
+
+
Expression
+
+
+ ←
+
+
+
+ {#each expression.split('') as char, i}
+ {#if i === cursorPosition}
+
+ {/if}
+ {char}
+ {/each}
+ {#if cursorPosition === expression.length}
+
+ {/if}
+
+
+
+ →
+
+
+ Del
+
+
+ Clear
+
+
+
+
+
+
Operators
+
+ {#each operators as op}
+ addOperator(op)}
+ >
+ {op}
+
+ {/each}
+
+
+
+ {#if errors.input}
+
+
{errors.input}:
+
+ {#each inputStatus as item (item.value)}
+
+ {item.value}
+
+ {/each}
+
+
+ {/if}
+
+
+ Save
+
+
+
+
\ No newline at end of file