Switch.svelte 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. <script lang="ts">
  2. import { untrack } from "svelte";
  3. import { getBoundProp, getOptionalValidationContext } from "@json-render/svelte";
  4. import type { BaseComponentProps } from "@json-render/svelte";
  5. import type { ShadcnProps } from "./catalog.js";
  6. import { createValidation } from "./helpers.js";
  7. import { Switch } from "./ui/switch/index.js";
  8. import { Label } from "./ui/label/index.js";
  9. interface Props extends BaseComponentProps<ShadcnProps<"Switch">> {}
  10. let { props, bindings, emit }: Props = $props();
  11. let localChecked = $state(untrack(() => Boolean(props.checked)));
  12. let errors = $state<string[]>([]);
  13. const validateOn = $derived((props.validateOn ?? "change") as "change" | "blur" | "submit");
  14. const validationCtx = getOptionalValidationContext();
  15. const validation = createValidation(
  16. validationCtx,
  17. untrack(() => bindings?.checked),
  18. untrack(() => props.checks ?? null),
  19. );
  20. $effect(() => {
  21. const on = validateOn;
  22. untrack(() => {
  23. if (validation.hasValidation) validation.register(on);
  24. });
  25. });
  26. const bound = getBoundProp<boolean>(
  27. () => props.checked ?? undefined,
  28. () => bindings?.checked,
  29. );
  30. const checked = $derived(bound.current ?? localChecked);
  31. function handleChange(value: boolean) {
  32. localChecked = value;
  33. bound.current = value;
  34. if (validateOn === "change") errors = validation.run(validateOn);
  35. emit("change");
  36. }
  37. </script>
  38. <div class="space-y-1">
  39. <div class="flex items-center gap-2">
  40. <Switch id={props.name} {checked} onCheckedChange={handleChange} />
  41. {#if props.label}
  42. <Label for={props.name} class="text-sm cursor-pointer">{props.label}</Label>
  43. {/if}
  44. </div>
  45. {#if errors.length > 0}
  46. <p class="text-sm text-destructive">{errors[0]}</p>
  47. {/if}
  48. </div>