Checkbox.svelte 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  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 { Checkbox } from "./ui/checkbox/index.js";
  8. import { Label } from "./ui/label/index.js";
  9. interface Props extends BaseComponentProps<ShadcnProps<"Checkbox">> {}
  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 | "indeterminate") {
  32. const next = value === true;
  33. localChecked = next;
  34. bound.current = next;
  35. if (validateOn === "change") errors = validation.run(validateOn);
  36. emit("change");
  37. }
  38. </script>
  39. <div class="space-y-1">
  40. <div class="flex items-center gap-2">
  41. <Checkbox
  42. id={props.name}
  43. {checked}
  44. onCheckedChange={handleChange}
  45. />
  46. <Label for={props.name} class="text-sm cursor-pointer">{props.label}</Label>
  47. </div>
  48. {#if errors.length > 0}
  49. <p class="text-sm text-destructive">{errors[0]}</p>
  50. {/if}
  51. </div>