All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.dist.index.mjs Maven / Gradle / Ivy

The newest version!
import { createAnatomy } from '@zag-js/anatomy';
import { isLeftClick, isModifierKey, getRelativePoint, trackPointerMove } from '@zag-js/dom-event';
import { createScope, query, getDataUrl, dataAttr, getEventTarget } from '@zag-js/dom-query';
import { createMachine } from '@zag-js/core';
import { createSplitProps, compact } from '@zag-js/utils';
import getStroke from 'perfect-freehand';
import { createProps } from '@zag-js/types';

// src/signature-pad.anatomy.ts
var anatomy = createAnatomy("signature-pad").parts(
  "root",
  "control",
  "segment",
  "segmentPath",
  "guide",
  "clearTrigger",
  "label"
);
var parts = anatomy.build();
var dom = createScope({
  getRootId: (ctx) => ctx.ids?.root ?? `signature-${ctx.id}`,
  getControlId: (ctx) => ctx.ids?.control ?? `signature-control-${ctx.id}`,
  getLabelId: (ctx) => ctx.ids?.label ?? `signature-label-${ctx.id}`,
  getHiddenInputId: (ctx) => ctx.ids?.hiddenInput ?? `signature-input-${ctx.id}`,
  getControlEl: (ctx) => dom.getById(ctx, dom.getControlId(ctx)),
  getSegmentEl: (ctx) => query(dom.getControlEl(ctx), "[data-part=segment]"),
  getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx)),
  getDataUrl: (ctx, options) => {
    if (ctx.isEmpty) return Promise.resolve("");
    return getDataUrl(dom.getSegmentEl(ctx), options);
  }
});

// src/signature-pad.connect.ts
var defaultTranslations = {
  control: "signature pad",
  clearTrigger: "clear signature"
};
function connect(state, send, normalize) {
  const drawing = state.matches("drawing");
  const empty = state.context.isEmpty;
  const interactive = state.context.isInteractive;
  const disabled = !!state.context.disabled;
  const translations = state.context.translations || defaultTranslations;
  return {
    empty,
    drawing,
    currentPath: state.context.currentPath,
    paths: state.context.paths,
    clear() {
      send({ type: "CLEAR" });
    },
    getDataUrl(type, quality) {
      return dom.getDataUrl(state.context, { type, quality });
    },
    getLabelProps() {
      return normalize.label({
        ...parts.label.attrs,
        id: dom.getLabelId(state.context),
        "data-disabled": dataAttr(disabled),
        htmlFor: dom.getHiddenInputId(state.context),
        onClick(event) {
          if (!interactive) return;
          if (event.defaultPrevented) return;
          const controlEl = dom.getControlEl(state.context);
          controlEl?.focus({ preventScroll: true });
        }
      });
    },
    getRootProps() {
      return normalize.element({
        ...parts.root.attrs,
        "data-disabled": dataAttr(disabled),
        id: dom.getRootId(state.context)
      });
    },
    getControlProps() {
      return normalize.element({
        ...parts.control.attrs,
        tabIndex: disabled ? void 0 : 0,
        id: dom.getControlId(state.context),
        role: "application",
        "aria-roledescription": "signature pad",
        "aria-label": translations.control,
        "aria-disabled": disabled,
        "data-disabled": dataAttr(disabled),
        onPointerDown(event) {
          if (!isLeftClick(event)) return;
          if (isModifierKey(event)) return;
          if (!interactive) return;
          const target = getEventTarget(event);
          if (target?.closest("[data-part=clear-trigger]")) return;
          event.currentTarget.setPointerCapture(event.pointerId);
          const point = { x: event.clientX, y: event.clientY };
          const { offset } = getRelativePoint(point, dom.getControlEl(state.context));
          send({ type: "POINTER_DOWN", point: offset, pressure: event.pressure });
        },
        onPointerUp(event) {
          if (!interactive) return;
          if (event.currentTarget.hasPointerCapture(event.pointerId)) {
            event.currentTarget.releasePointerCapture(event.pointerId);
          }
        },
        style: {
          position: "relative",
          touchAction: "none",
          userSelect: "none",
          WebkitUserSelect: "none"
        }
      });
    },
    getSegmentProps() {
      return normalize.svg({
        ...parts.segment.attrs,
        style: {
          position: "absolute",
          top: 0,
          left: 0,
          width: "100%",
          height: "100%",
          pointerEvents: "none",
          fill: state.context.drawing.fill
        }
      });
    },
    getSegmentPathProps(props2) {
      return normalize.path({
        ...parts.segmentPath.attrs,
        d: props2.path
      });
    },
    getGuideProps() {
      return normalize.element({
        ...parts.guide.attrs,
        "data-disabled": dataAttr(disabled)
      });
    },
    getClearTriggerProps() {
      return normalize.button({
        ...parts.clearTrigger.attrs,
        type: "button",
        "aria-label": translations.clearTrigger,
        hidden: !state.context.paths.length || drawing,
        disabled,
        onClick() {
          send({ type: "CLEAR" });
        }
      });
    },
    getHiddenInputProps(props2) {
      return normalize.input({
        id: dom.getHiddenInputId(state.context),
        type: "text",
        hidden: true,
        disabled,
        required: state.context.required,
        readOnly: state.context.readOnly,
        name: state.context.name,
        value: props2.value
      });
    }
  };
}

// src/get-svg-path.ts
var average = (a, b) => (a + b) / 2;
function getSvgPathFromStroke(points, closed = true) {
  const len = points.length;
  if (len < 4) {
    return "";
  }
  let a = points[0];
  let b = points[1];
  const c = points[2];
  let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed(2)},${b[1].toFixed(2)} ${average(b[0], c[0]).toFixed(2)},${average(
    b[1],
    c[1]
  ).toFixed(2)} T`;
  for (let i = 2, max = len - 1; i < max; i++) {
    a = points[i];
    b = points[i + 1];
    result += `${average(a[0], b[0]).toFixed(2)},${average(a[1], b[1]).toFixed(2)} `;
  }
  if (closed) {
    result += "Z";
  }
  return result;
}

// src/signature-pad.machine.ts
function machine(userContext) {
  const ctx = compact(userContext);
  return createMachine(
    {
      id: "signature-pad",
      initial: "idle",
      context: {
        readOnly: false,
        disabled: false,
        ...ctx,
        paths: [],
        currentPoints: [],
        currentPath: null,
        drawing: {
          size: 2,
          simulatePressure: false,
          thinning: 0.7,
          smoothing: 0.4,
          streamline: 0.6,
          ...ctx.drawing
        }
      },
      computed: {
        isInteractive: (ctx2) => !(ctx2.disabled || ctx2.readOnly),
        isEmpty: (ctx2) => ctx2.paths.length === 0
      },
      on: {
        CLEAR: {
          actions: ["clearPoints", "invokeOnDrawEnd", "focusCanvasEl"]
        }
      },
      states: {
        idle: {
          on: {
            POINTER_DOWN: {
              target: "drawing",
              actions: ["addPoint"]
            }
          }
        },
        drawing: {
          activities: ["trackPointerMove"],
          on: {
            POINTER_MOVE: {
              actions: ["addPoint", "invokeOnDraw"]
            },
            POINTER_UP: {
              target: "idle",
              actions: ["endStroke", "invokeOnDrawEnd"]
            }
          }
        }
      }
    },
    {
      activities: {
        trackPointerMove(ctx2, _evt, { send }) {
          const doc = dom.getDoc(ctx2);
          return trackPointerMove(doc, {
            onPointerMove({ event, point }) {
              const { offset } = getRelativePoint(point, dom.getControlEl(ctx2));
              send({ type: "POINTER_MOVE", point: offset, pressure: event.pressure });
            },
            onPointerUp() {
              send({ type: "POINTER_UP" });
            }
          });
        }
      },
      actions: {
        addPoint(ctx2, evt) {
          ctx2.currentPoints.push(evt.point);
          const stroke = getStroke(ctx2.currentPoints, ctx2.drawing);
          ctx2.currentPath = getSvgPathFromStroke(stroke);
        },
        endStroke(ctx2) {
          ctx2.paths.push(ctx2.currentPath);
          ctx2.currentPoints = [];
          ctx2.currentPath = null;
        },
        clearPoints(ctx2) {
          ctx2.currentPoints = [];
          ctx2.paths = [];
        },
        focusCanvasEl(ctx2) {
          queueMicrotask(() => {
            dom.getControlEl(ctx2)?.focus({ preventScroll: true });
          });
        },
        invokeOnDraw(ctx2) {
          ctx2.onDraw?.({
            paths: [...ctx2.paths, ctx2.currentPath]
          });
        },
        invokeOnDrawEnd(ctx2) {
          ctx2.onDrawEnd?.({
            paths: [...ctx2.paths],
            getDataUrl(type, quality = 0.92) {
              return dom.getDataUrl(ctx2, { type, quality });
            }
          });
        }
      }
    }
  );
}
var props = createProps()([
  "dir",
  "disabled",
  "drawing",
  "getRootNode",
  "id",
  "ids",
  "name",
  "onDraw",
  "onDrawEnd",
  "readOnly",
  "required",
  "translations"
]);
var splitProps = createSplitProps(props);

export { anatomy, connect, machine, props, splitProps };




© 2015 - 2025 Weber Informatics LLC | Privacy Policy