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

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

The newest version!
'use strict';

var anatomy$1 = require('@zag-js/anatomy');
var domQuery = require('@zag-js/dom-query');
var core = require('@zag-js/core');
var interactOutside = require('@zag-js/interact-outside');
var utils = require('@zag-js/utils');
var types = require('@zag-js/types');

// src/editable.anatomy.ts
var anatomy = anatomy$1.createAnatomy("editable").parts(
  "root",
  "area",
  "label",
  "preview",
  "input",
  "editTrigger",
  "submitTrigger",
  "cancelTrigger",
  "control"
);
var parts = anatomy.build();
var dom = domQuery.createScope({
  getRootId: (ctx) => ctx.ids?.root ?? `editable:${ctx.id}`,
  getAreaId: (ctx) => ctx.ids?.area ?? `editable:${ctx.id}:area`,
  getLabelId: (ctx) => ctx.ids?.label ?? `editable:${ctx.id}:label`,
  getPreviewId: (ctx) => ctx.ids?.preview ?? `editable:${ctx.id}:preview`,
  getInputId: (ctx) => ctx.ids?.input ?? `editable:${ctx.id}:input`,
  getControlId: (ctx) => ctx.ids?.control ?? `editable:${ctx.id}:control`,
  getSubmitTriggerId: (ctx) => ctx.ids?.submitTrigger ?? `editable:${ctx.id}:submit`,
  getCancelTriggerId: (ctx) => ctx.ids?.cancelTrigger ?? `editable:${ctx.id}:cancel`,
  getEditTriggerId: (ctx) => ctx.ids?.editTrigger ?? `editable:${ctx.id}:edit`,
  getInputEl: (ctx) => dom.getById(ctx, dom.getInputId(ctx)),
  getPreviewEl: (ctx) => dom.getById(ctx, dom.getPreviewId(ctx)),
  getSubmitTriggerEl: (ctx) => dom.getById(ctx, dom.getSubmitTriggerId(ctx)),
  getCancelTriggerEl: (ctx) => dom.getById(ctx, dom.getCancelTriggerId(ctx)),
  getEditTriggerEl: (ctx) => dom.getById(ctx, dom.getEditTriggerId(ctx))
});

// src/editable.connect.ts
function connect(state, send, normalize) {
  const disabled = state.context.disabled;
  const interactive = state.context.isInteractive;
  const readOnly = state.context.readOnly;
  const invalid = state.context.invalid;
  const autoResize = state.context.autoResize;
  const translations = state.context.translations;
  const editing = state.matches("edit");
  const placeholderProp = state.context.placeholder;
  const placeholder = typeof placeholderProp === "string" ? { edit: placeholderProp, preview: placeholderProp } : placeholderProp;
  const value = state.context.value;
  const empty = value.trim() === "";
  const valueText = empty ? placeholder?.preview ?? "" : value;
  return {
    editing,
    empty,
    value,
    valueText,
    setValue(value2) {
      send({ type: "VALUE.SET", value: value2, src: "setValue" });
    },
    clearValue() {
      send({ type: "VALUE.SET", value: "", src: "clearValue" });
    },
    edit() {
      if (!interactive) return;
      send("EDIT");
    },
    cancel() {
      if (!interactive) return;
      send("CANCEL");
    },
    submit() {
      if (!interactive) return;
      send("SUBMIT");
    },
    getRootProps() {
      return normalize.element({
        ...parts.root.attrs,
        id: dom.getRootId(state.context),
        dir: state.context.dir
      });
    },
    getAreaProps() {
      return normalize.element({
        ...parts.area.attrs,
        id: dom.getAreaId(state.context),
        dir: state.context.dir,
        style: autoResize ? { display: "inline-grid" } : void 0,
        "data-focus": domQuery.dataAttr(editing),
        "data-disabled": domQuery.dataAttr(disabled),
        "data-placeholder-shown": domQuery.dataAttr(empty)
      });
    },
    getLabelProps() {
      return normalize.label({
        ...parts.label.attrs,
        id: dom.getLabelId(state.context),
        dir: state.context.dir,
        htmlFor: dom.getInputId(state.context),
        "data-focus": domQuery.dataAttr(editing),
        "data-invalid": domQuery.dataAttr(invalid),
        onClick() {
          if (editing) return;
          const previewEl = dom.getPreviewEl(state.context);
          previewEl?.focus({ preventScroll: true });
        }
      });
    },
    getInputProps() {
      return normalize.input({
        ...parts.input.attrs,
        dir: state.context.dir,
        "aria-label": translations.input,
        name: state.context.name,
        form: state.context.form,
        id: dom.getInputId(state.context),
        hidden: autoResize ? void 0 : !editing,
        placeholder: placeholder?.edit,
        maxLength: state.context.maxLength,
        required: state.context.required,
        disabled,
        "data-disabled": domQuery.dataAttr(disabled),
        readOnly,
        "data-readonly": domQuery.dataAttr(readOnly),
        "aria-invalid": domQuery.ariaAttr(invalid),
        "data-invalid": domQuery.dataAttr(invalid),
        "data-autoresize": domQuery.dataAttr(autoResize),
        defaultValue: value,
        size: autoResize ? 1 : void 0,
        onChange(event) {
          send({ type: "VALUE.SET", src: "input.change", value: event.currentTarget.value });
        },
        onKeyDown(event) {
          if (event.defaultPrevented) return;
          if (domQuery.isComposingEvent(event)) return;
          const keyMap = {
            Escape() {
              send("CANCEL");
              event.preventDefault();
            },
            Enter(event2) {
              if (!state.context.submitOnEnter) return;
              const { localName } = event2.currentTarget;
              if (localName === "textarea") {
                const submitMod = domQuery.isApple() ? event2.metaKey : event2.ctrlKey;
                if (!submitMod) return;
                send({ type: "SUBMIT", src: "keydown.enter" });
                return;
              }
              if (localName === "input" && !event2.shiftKey && !event2.metaKey) {
                send({ type: "SUBMIT", src: "keydown.enter" });
                event2.preventDefault();
              }
            }
          };
          const exec = keyMap[event.key];
          if (exec) {
            exec(event);
          }
        },
        style: autoResize ? {
          gridArea: "1 / 1 / auto / auto",
          visibility: !editing ? "hidden" : void 0
        } : void 0
      });
    },
    getPreviewProps() {
      return normalize.element({
        id: dom.getPreviewId(state.context),
        ...parts.preview.attrs,
        dir: state.context.dir,
        "data-placeholder-shown": domQuery.dataAttr(empty),
        "aria-readonly": domQuery.ariaAttr(readOnly),
        "data-readonly": domQuery.dataAttr(disabled),
        "data-disabled": domQuery.dataAttr(disabled),
        "aria-disabled": domQuery.ariaAttr(disabled),
        "aria-invalid": domQuery.ariaAttr(invalid),
        "data-invalid": domQuery.dataAttr(invalid),
        "aria-label": translations.edit,
        "data-autoresize": domQuery.dataAttr(autoResize),
        children: valueText,
        hidden: autoResize ? void 0 : editing,
        tabIndex: interactive ? 0 : void 0,
        onClick() {
          if (!interactive) return;
          if (state.context.activationMode !== "click") return;
          send({ type: "EDIT", src: "click" });
        },
        onFocus() {
          if (!interactive) return;
          if (state.context.activationMode !== "focus") return;
          send({ type: "EDIT", src: "focus" });
        },
        onDoubleClick(event) {
          if (event.defaultPrevented) return;
          if (!interactive) return;
          if (state.context.activationMode !== "dblclick") return;
          send({ type: "EDIT", src: "dblclick" });
        },
        style: autoResize ? {
          whiteSpace: "pre",
          userSelect: "none",
          gridArea: "1 / 1 / auto / auto",
          visibility: editing ? "hidden" : void 0,
          // in event the preview overflow's the parent element
          overflow: "hidden",
          textOverflow: "ellipsis"
        } : void 0
      });
    },
    getEditTriggerProps() {
      return normalize.button({
        ...parts.editTrigger.attrs,
        id: dom.getEditTriggerId(state.context),
        dir: state.context.dir,
        "aria-label": translations.edit,
        hidden: editing,
        type: "button",
        disabled,
        onClick(event) {
          if (event.defaultPrevented) return;
          if (!interactive) return;
          send({ type: "EDIT", src: "edit.click" });
        }
      });
    },
    getControlProps() {
      return normalize.element({
        id: dom.getControlId(state.context),
        ...parts.control.attrs,
        dir: state.context.dir
      });
    },
    getSubmitTriggerProps() {
      return normalize.button({
        ...parts.submitTrigger.attrs,
        dir: state.context.dir,
        id: dom.getSubmitTriggerId(state.context),
        "aria-label": translations.submit,
        hidden: !editing,
        disabled,
        type: "button",
        onClick(event) {
          if (event.defaultPrevented) return;
          if (!interactive) return;
          send({ type: "SUBMIT", src: "submit.click" });
        }
      });
    },
    getCancelTriggerProps() {
      return normalize.button({
        ...parts.cancelTrigger.attrs,
        dir: state.context.dir,
        "aria-label": translations.cancel,
        id: dom.getCancelTriggerId(state.context),
        hidden: !editing,
        type: "button",
        disabled,
        onClick(event) {
          if (event.defaultPrevented) return;
          if (!interactive) return;
          send({ type: "CANCEL", src: "cancel.click" });
        }
      });
    }
  };
}
var submitOnEnter = (ctx) => ["both", "enter"].includes(ctx.submitMode);
var submitOnBlur = (ctx) => ["both", "blur"].includes(ctx.submitMode);
function machine(userContext) {
  const ctx = utils.compact(userContext);
  return core.createMachine(
    {
      id: "editable",
      initial: ctx.edit ? "edit" : "preview",
      entry: ctx.edit ? ["focusInput"] : void 0,
      context: {
        activationMode: "focus",
        submitMode: "both",
        value: "",
        previousValue: "",
        selectOnFocus: true,
        disabled: false,
        readOnly: false,
        ...ctx,
        translations: {
          input: "editable input",
          edit: "edit",
          submit: "submit",
          cancel: "cancel",
          ...ctx.translations
        }
      },
      watch: {
        value: ["syncInputValue"],
        edit: ["toggleEditing"]
      },
      computed: {
        submitOnEnter,
        submitOnBlur,
        isInteractive: (ctx2) => !(ctx2.disabled || ctx2.readOnly)
      },
      on: {
        "VALUE.SET": {
          actions: "setValue"
        }
      },
      states: {
        preview: {
          // https://bugzilla.mozilla.org/show_bug.cgi?id=559561
          entry: ["blurInputIfNeeded"],
          on: {
            "CONTROLLED.EDIT": {
              target: "edit",
              actions: ["setPreviousValue", "focusInput"]
            },
            EDIT: [
              {
                guard: "isEditControlled",
                actions: ["invokeOnEdit"]
              },
              {
                target: "edit",
                actions: ["setPreviousValue", "focusInput", "invokeOnEdit"]
              }
            ]
          }
        },
        edit: {
          activities: ["trackInteractOutside"],
          on: {
            "CONTROLLED.PREVIEW": [
              {
                guard: "isSubmitEvent",
                target: "preview",
                actions: ["setPreviousValue", "restoreFocus", "invokeOnSubmit"]
              },
              {
                target: "preview",
                actions: ["revertValue", "restoreFocus", "invokeOnCancel"]
              }
            ],
            CANCEL: [
              {
                guard: "isEditControlled",
                actions: ["invokeOnPreview"]
              },
              {
                target: "preview",
                actions: ["revertValue", "restoreFocus", "invokeOnCancel", "invokeOnPreview"]
              }
            ],
            SUBMIT: [
              {
                guard: "isEditControlled",
                actions: ["invokeOnPreview"]
              },
              {
                target: "preview",
                actions: ["setPreviousValue", "restoreFocus", "invokeOnSubmit", "invokeOnPreview"]
              }
            ]
          }
        }
      }
    },
    {
      guards: {
        isEditControlled: (ctx2) => !!ctx2["edit.controlled"],
        isSubmitEvent: (_ctx, evt) => evt.previousEvent?.type === "SUBMIT"
      },
      activities: {
        trackInteractOutside(ctx2, _evt, { send }) {
          return interactOutside.trackInteractOutside(dom.getInputEl(ctx2), {
            exclude(target) {
              const ignore = [dom.getCancelTriggerEl(ctx2), dom.getSubmitTriggerEl(ctx2)];
              return ignore.some((el) => domQuery.contains(el, target));
            },
            onFocusOutside: ctx2.onFocusOutside,
            onPointerDownOutside: ctx2.onPointerDownOutside,
            onInteractOutside(event) {
              ctx2.onInteractOutside?.(event);
              if (event.defaultPrevented) return;
              const { focusable } = event.detail;
              send({ type: submitOnBlur(ctx2) ? "SUBMIT" : "CANCEL", src: "interact-outside", focusable });
            }
          });
        }
      },
      actions: {
        restoreFocus(ctx2, evt) {
          if (evt.focusable) return;
          domQuery.raf(() => {
            const finalEl = ctx2.finalFocusEl?.() ?? dom.getEditTriggerEl(ctx2);
            finalEl?.focus({ preventScroll: true });
          });
        },
        focusInput(ctx2) {
          domQuery.raf(() => {
            const inputEl = dom.getInputEl(ctx2);
            if (!inputEl) return;
            if (ctx2.selectOnFocus) {
              inputEl.select();
            } else {
              inputEl.focus({ preventScroll: true });
            }
          });
        },
        invokeOnCancel(ctx2) {
          ctx2.onValueRevert?.({ value: ctx2.previousValue });
        },
        invokeOnSubmit(ctx2) {
          ctx2.onValueCommit?.({ value: ctx2.value });
        },
        invokeOnEdit(ctx2) {
          ctx2.onEditChange?.({ edit: true });
        },
        invokeOnPreview(ctx2) {
          ctx2.onEditChange?.({ edit: false });
        },
        toggleEditing(ctx2, evt, { send }) {
          send({ type: ctx2.edit ? "CONTROLLED.EDIT" : "CONTROLLED.PREVIEW", previousEvent: evt });
        },
        syncInputValue(ctx2) {
          sync.value(ctx2);
        },
        setValue(ctx2, evt) {
          const value = ctx2.maxLength != null ? evt.value.slice(0, ctx2.maxLength) : evt.value;
          set.value(ctx2, value);
        },
        setPreviousValue(ctx2) {
          ctx2.previousValue = ctx2.value;
        },
        revertValue(ctx2) {
          set.value(ctx2, ctx2.previousValue);
        },
        blurInputIfNeeded(ctx2) {
          dom.getInputEl(ctx2)?.blur();
        }
      }
    }
  );
}
var sync = {
  value: (ctx) => {
    const inputEl = dom.getInputEl(ctx);
    dom.setValue(inputEl, ctx.value);
  }
};
var invoke = {
  change(ctx) {
    ctx.onValueChange?.({ value: ctx.value });
    sync.value(ctx);
  }
};
var set = {
  value(ctx, value) {
    if (utils.isEqual(ctx.value, value)) return;
    ctx.value = value;
    invoke.change(ctx);
  }
};
var props = types.createProps()([
  "activationMode",
  "autoResize",
  "dir",
  "disabled",
  "finalFocusEl",
  "form",
  "getRootNode",
  "id",
  "ids",
  "invalid",
  "maxLength",
  "name",
  "onEditChange",
  "onFocusOutside",
  "onInteractOutside",
  "onPointerDownOutside",
  "onValueChange",
  "onValueCommit",
  "onValueRevert",
  "placeholder",
  "readOnly",
  "required",
  "selectOnFocus",
  "edit",
  "edit.controlled",
  "submitMode",
  "translations",
  "value"
]);
var splitProps = utils.createSplitProps(props);

exports.anatomy = anatomy;
exports.connect = connect;
exports.machine = machine;
exports.props = props;
exports.splitProps = splitProps;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy