@zag-js/listbox
Version: 
Core logic for the listbox widget implemented as a state machine
761 lines (755 loc) • 27.1 kB
JavaScript
;
var anatomy$1 = require('@zag-js/anatomy');
var collection$1 = require('@zag-js/collection');
var domQuery = require('@zag-js/dom-query');
var utils = require('@zag-js/utils');
var core = require('@zag-js/core');
var focusVisible = require('@zag-js/focus-visible');
var types = require('@zag-js/types');
// src/listbox.anatomy.ts
var anatomy = anatomy$1.createAnatomy("listbox").parts(
  "label",
  "input",
  "item",
  "itemText",
  "itemIndicator",
  "itemGroup",
  "itemGroupLabel",
  "content",
  "root",
  "valueText"
);
var parts = anatomy.build();
var collection = (options) => {
  return new collection$1.ListCollection(options);
};
collection.empty = () => {
  return new collection$1.ListCollection({ items: [] });
};
var gridCollection = (options) => {
  return new collection$1.GridCollection(options);
};
gridCollection.empty = () => {
  return new collection$1.GridCollection({ items: [], columnCount: 0 });
};
// src/listbox.dom.ts
var getRootId = (ctx) => ctx.ids?.root ?? `select:${ctx.id}`;
var getContentId = (ctx) => ctx.ids?.content ?? `select:${ctx.id}:content`;
var getLabelId = (ctx) => ctx.ids?.label ?? `select:${ctx.id}:label`;
var getItemId = (ctx, id) => ctx.ids?.item?.(id) ?? `select:${ctx.id}:option:${id}`;
var getItemGroupId = (ctx, id) => ctx.ids?.itemGroup?.(id) ?? `select:${ctx.id}:optgroup:${id}`;
var getItemGroupLabelId = (ctx, id) => ctx.ids?.itemGroupLabel?.(id) ?? `select:${ctx.id}:optgroup-label:${id}`;
var getContentEl = (ctx) => ctx.getById(getContentId(ctx));
var getItemEl = (ctx, id) => ctx.getById(getItemId(ctx, id));
// src/listbox.connect.ts
function connect(service, normalize) {
  const { context, prop, scope, computed, send, refs } = service;
  const disabled = prop("disabled");
  const collection2 = prop("collection");
  const layout = collection$1.isGridCollection(collection2) ? "grid" : "list";
  const focused = context.get("focused");
  const focusVisible = refs.get("focusVisible") && focused;
  const value = context.get("value");
  const selectedItems = context.get("selectedItems");
  const highlightedValue = context.get("highlightedValue");
  const highlightedItem = context.get("highlightedItem");
  const isTypingAhead = computed("isTypingAhead");
  const interactive = computed("isInteractive");
  const ariaActiveDescendant = highlightedValue ? getItemId(scope, highlightedValue) : void 0;
  function getItemState(props2) {
    const itemDisabled = collection2.getItemDisabled(props2.item);
    const value2 = collection2.getItemValue(props2.item);
    utils.ensure(value2, () => `[zag-js] No value found for item ${JSON.stringify(props2.item)}`);
    const highlighted = highlightedValue === value2;
    return {
      value: value2,
      disabled: Boolean(disabled || itemDisabled),
      focused: highlighted && focused,
      focusVisible: highlighted && focusVisible,
      // deprecated
      highlighted: highlighted && focusVisible,
      selected: context.get("value").includes(value2)
    };
  }
  return {
    empty: value.length === 0,
    highlightedItem,
    highlightedValue,
    clearHighlightedValue() {
      send({ type: "HIGHLIGHTED_VALUE.SET", value: null });
    },
    selectedItems,
    hasSelectedItems: computed("hasSelectedItems"),
    value,
    valueAsString: computed("valueAsString"),
    collection: collection2,
    disabled: !!disabled,
    selectValue(value2) {
      send({ type: "ITEM.SELECT", value: value2 });
    },
    setValue(value2) {
      send({ type: "VALUE.SET", value: value2 });
    },
    selectAll() {
      if (!computed("multiple")) {
        throw new Error("[zag-js] Cannot select all items in a single-select listbox");
      }
      send({ type: "VALUE.SET", value: collection2.getValues() });
    },
    highlightValue(value2) {
      send({ type: "HIGHLIGHTED_VALUE.SET", value: value2 });
    },
    clearValue(value2) {
      if (value2) {
        send({ type: "ITEM.CLEAR", value: value2 });
      } else {
        send({ type: "VALUE.CLEAR" });
      }
    },
    getItemState,
    getRootProps() {
      return normalize.element({
        ...parts.root.attrs,
        dir: prop("dir"),
        id: getRootId(scope),
        "data-orientation": prop("orientation"),
        "data-disabled": domQuery.dataAttr(disabled)
      });
    },
    getInputProps(props2 = {}) {
      return normalize.input({
        ...parts.input.attrs,
        dir: prop("dir"),
        disabled,
        "data-disabled": domQuery.dataAttr(disabled),
        autoComplete: "off",
        autoCorrect: "off",
        "aria-haspopup": "listbox",
        "aria-controls": getContentId(scope),
        "aria-autocomplete": "list",
        "aria-activedescendant": ariaActiveDescendant,
        spellCheck: false,
        enterKeyHint: "go",
        onFocus() {
          queueMicrotask(() => {
            send({ type: "INPUT.FOCUS" });
          });
        },
        onBlur() {
          send({ type: "CONTENT.BLUR", src: "input" });
        },
        onInput(event) {
          if (!props2.autoHighlight) return;
          const node = event.currentTarget;
          queueMicrotask(() => {
            if (!node.isConnected) return;
            send({
              type: "HIGHLIGHTED_VALUE.SET",
              value: node.value ? prop("collection").firstValue : null
            });
          });
        },
        onKeyDown(event) {
          if (event.defaultPrevented) return;
          if (domQuery.isComposingEvent(event)) return;
          const nativeEvent = domQuery.getNativeEvent(event);
          const forwardEvent = () => {
            event.preventDefault();
            const win = scope.getWin();
            const keyboardEvent = new win.KeyboardEvent(nativeEvent.type, nativeEvent);
            getContentEl(scope)?.dispatchEvent(keyboardEvent);
          };
          switch (nativeEvent.key) {
            case "ArrowLeft":
            case "ArrowRight": {
              if (!collection$1.isGridCollection(collection2)) return;
              if (event.ctrlKey) return;
              forwardEvent();
            }
            case "Home":
            case "End": {
              if (highlightedValue == null && event.shiftKey) return;
              forwardEvent();
            }
            case "ArrowDown":
            case "ArrowUp": {
              forwardEvent();
              break;
            }
            case "Enter":
              event.preventDefault();
              send({ type: "ITEM.CLICK", value: highlightedValue });
              break;
          }
        }
      });
    },
    getLabelProps() {
      return normalize.element({
        dir: prop("dir"),
        id: getLabelId(scope),
        ...parts.label.attrs,
        "data-disabled": domQuery.dataAttr(disabled)
      });
    },
    getValueTextProps() {
      return normalize.element({
        ...parts.valueText.attrs,
        dir: prop("dir"),
        "data-disabled": domQuery.dataAttr(disabled)
      });
    },
    getItemProps(props2) {
      const itemState = getItemState(props2);
      return normalize.element({
        id: getItemId(scope, itemState.value),
        role: "option",
        ...parts.item.attrs,
        dir: prop("dir"),
        "data-value": itemState.value,
        "aria-selected": itemState.selected,
        "data-selected": domQuery.dataAttr(itemState.selected),
        "data-layout": layout,
        "data-state": itemState.selected ? "checked" : "unchecked",
        "data-orientation": prop("orientation"),
        "data-highlighted": domQuery.dataAttr(itemState.highlighted),
        "data-disabled": domQuery.dataAttr(itemState.disabled),
        "aria-disabled": domQuery.ariaAttr(itemState.disabled),
        onPointerMove(event) {
          if (!props2.highlightOnHover) return;
          if (itemState.disabled || event.pointerType !== "mouse") return;
          if (itemState.highlighted) return;
          send({ type: "ITEM.POINTER_MOVE", value: itemState.value });
        },
        onMouseDown(event) {
          event.preventDefault();
          getContentEl(scope)?.focus();
        },
        onClick(event) {
          if (event.defaultPrevented) return;
          if (itemState.disabled) return;
          send({
            type: "ITEM.CLICK",
            value: itemState.value,
            shiftKey: event.shiftKey,
            anchorValue: highlightedValue,
            metaKey: domQuery.isCtrlOrMetaKey(event)
          });
        }
      });
    },
    getItemTextProps(props2) {
      const itemState = getItemState(props2);
      return normalize.element({
        ...parts.itemText.attrs,
        "data-state": itemState.selected ? "checked" : "unchecked",
        "data-disabled": domQuery.dataAttr(itemState.disabled),
        "data-highlighted": domQuery.dataAttr(itemState.highlighted)
      });
    },
    getItemIndicatorProps(props2) {
      const itemState = getItemState(props2);
      return normalize.element({
        ...parts.itemIndicator.attrs,
        "aria-hidden": true,
        "data-state": itemState.selected ? "checked" : "unchecked",
        hidden: !itemState.selected
      });
    },
    getItemGroupLabelProps(props2) {
      const { htmlFor } = props2;
      return normalize.element({
        ...parts.itemGroupLabel.attrs,
        id: getItemGroupLabelId(scope, htmlFor),
        dir: prop("dir"),
        role: "presentation"
      });
    },
    getItemGroupProps(props2) {
      const { id } = props2;
      return normalize.element({
        ...parts.itemGroup.attrs,
        "data-disabled": domQuery.dataAttr(disabled),
        "data-orientation": prop("orientation"),
        "data-empty": domQuery.dataAttr(collection2.size === 0),
        id: getItemGroupId(scope, id),
        "aria-labelledby": getItemGroupLabelId(scope, id),
        role: "group",
        dir: prop("dir")
      });
    },
    getContentProps() {
      return normalize.element({
        dir: prop("dir"),
        id: getContentId(scope),
        role: "listbox",
        ...parts.content.attrs,
        "data-activedescendant": ariaActiveDescendant,
        "aria-activedescendant": ariaActiveDescendant,
        "data-orientation": prop("orientation"),
        "aria-multiselectable": computed("multiple") ? true : void 0,
        "aria-labelledby": getLabelId(scope),
        tabIndex: 0,
        "data-layout": layout,
        "data-empty": domQuery.dataAttr(collection2.size === 0),
        style: {
          "--column-count": collection$1.isGridCollection(collection2) ? collection2.columnCount : 1
        },
        onFocus() {
          send({ type: "CONTENT.FOCUS" });
        },
        onBlur() {
          send({ type: "CONTENT.BLUR" });
        },
        onKeyDown(event) {
          if (!interactive) return;
          if (!domQuery.contains(event.currentTarget, domQuery.getEventTarget(event))) return;
          const shiftKey = event.shiftKey;
          const keyMap = {
            ArrowUp(event2) {
              let nextValue = null;
              if (collection$1.isGridCollection(collection2) && highlightedValue) {
                nextValue = collection2.getPreviousRowValue(highlightedValue);
              } else if (highlightedValue) {
                nextValue = collection2.getPreviousValue(highlightedValue);
              }
              if (!nextValue && (prop("loopFocus") || !highlightedValue)) {
                nextValue = collection2.lastValue;
              }
              if (!nextValue) return;
              event2.preventDefault();
              send({ type: "NAVIGATE", value: nextValue, shiftKey, anchorValue: highlightedValue });
            },
            ArrowDown(event2) {
              let nextValue = null;
              if (collection$1.isGridCollection(collection2) && highlightedValue) {
                nextValue = collection2.getNextRowValue(highlightedValue);
              } else if (highlightedValue) {
                nextValue = collection2.getNextValue(highlightedValue);
              }
              if (!nextValue && (prop("loopFocus") || !highlightedValue)) {
                nextValue = collection2.firstValue;
              }
              if (!nextValue) return;
              event2.preventDefault();
              send({ type: "NAVIGATE", value: nextValue, shiftKey, anchorValue: highlightedValue });
            },
            ArrowLeft() {
              if (!collection$1.isGridCollection(collection2) && prop("orientation") === "vertical") return;
              let nextValue = highlightedValue ? collection2.getPreviousValue(highlightedValue) : null;
              if (!nextValue && prop("loopFocus")) {
                nextValue = collection2.lastValue;
              }
              if (!nextValue) return;
              event.preventDefault();
              send({ type: "NAVIGATE", value: nextValue, shiftKey, anchorValue: highlightedValue });
            },
            ArrowRight() {
              if (!collection$1.isGridCollection(collection2) && prop("orientation") === "vertical") return;
              let nextValue = highlightedValue ? collection2.getNextValue(highlightedValue) : null;
              if (!nextValue && prop("loopFocus")) {
                nextValue = collection2.firstValue;
              }
              if (!nextValue) return;
              event.preventDefault();
              send({ type: "NAVIGATE", value: nextValue, shiftKey, anchorValue: highlightedValue });
            },
            Home(event2) {
              event2.preventDefault();
              let nextValue = collection2.firstValue;
              send({ type: "NAVIGATE", value: nextValue, shiftKey, anchorValue: highlightedValue });
            },
            End(event2) {
              event2.preventDefault();
              let nextValue = collection2.lastValue;
              send({ type: "NAVIGATE", value: nextValue, shiftKey, anchorValue: highlightedValue });
            },
            Enter() {
              send({ type: "ITEM.CLICK", value: highlightedValue });
            },
            a(event2) {
              if (domQuery.isCtrlOrMetaKey(event2) && computed("multiple") && !prop("disallowSelectAll")) {
                event2.preventDefault();
                send({ type: "VALUE.SET", value: collection2.getValues() });
              }
            },
            Space(event2) {
              if (isTypingAhead && prop("typeahead")) {
                send({ type: "CONTENT.TYPEAHEAD", key: event2.key });
              } else {
                keyMap.Enter?.(event2);
              }
            },
            Escape(event2) {
              if (prop("deselectable") && value.length > 0) {
                event2.preventDefault();
                event2.stopPropagation();
                send({ type: "VALUE.CLEAR" });
              }
            }
          };
          const exec = keyMap[domQuery.getEventKey(event)];
          if (exec) {
            exec(event);
            return;
          }
          const target = domQuery.getEventTarget(event);
          if (domQuery.isEditableElement(target)) {
            return;
          }
          if (domQuery.getByTypeahead.isValidEvent(event) && prop("typeahead")) {
            send({ type: "CONTENT.TYPEAHEAD", key: event.key });
            event.preventDefault();
          }
        }
      });
    }
  };
}
var { guards, createMachine } = core.setup();
var { or } = guards;
var machine = createMachine({
  props({ props: props2 }) {
    return {
      loopFocus: false,
      composite: true,
      defaultValue: [],
      multiple: false,
      typeahead: true,
      collection: collection.empty(),
      orientation: "vertical",
      selectionMode: "single",
      ...props2
    };
  },
  context({ prop, bindable }) {
    return {
      value: bindable(() => ({
        defaultValue: prop("defaultValue"),
        value: prop("value"),
        isEqual: utils.isEqual,
        onChange(value) {
          const items = prop("collection").findMany(value);
          return prop("onValueChange")?.({ value, items });
        }
      })),
      highlightedValue: bindable(() => ({
        defaultValue: prop("defaultHighlightedValue") || null,
        value: prop("highlightedValue"),
        sync: true,
        onChange(value) {
          prop("onHighlightChange")?.({
            highlightedValue: value,
            highlightedItem: prop("collection").find(value),
            highlightedIndex: prop("collection").indexOf(value)
          });
        }
      })),
      highlightedItem: bindable(() => ({
        defaultValue: null
      })),
      selectedItems: bindable(() => {
        const value = prop("value") ?? prop("defaultValue") ?? [];
        const items = prop("collection").findMany(value);
        return { defaultValue: items };
      }),
      focused: bindable(() => ({
        sync: true,
        defaultValue: false
      }))
    };
  },
  refs() {
    return {
      typeahead: { ...domQuery.getByTypeahead.defaultOptions },
      focusVisible: false
    };
  },
  computed: {
    hasSelectedItems: ({ context }) => context.get("value").length > 0,
    isTypingAhead: ({ refs }) => refs.get("typeahead").keysSoFar !== "",
    isInteractive: ({ prop }) => !prop("disabled"),
    selection: ({ context, prop }) => {
      const selection = new collection$1.Selection(context.get("value"));
      selection.selectionMode = prop("selectionMode");
      selection.deselectable = !!prop("deselectable");
      return selection;
    },
    multiple: ({ prop }) => prop("selectionMode") === "multiple" || prop("selectionMode") === "extended",
    valueAsString: ({ context, prop }) => prop("collection").stringifyItems(context.get("selectedItems"))
  },
  initialState() {
    return "idle";
  },
  watch({ context, prop, track, action }) {
    track([() => context.get("value").toString()], () => {
      action(["syncSelectedItems"]);
    });
    track([() => context.get("highlightedValue")], () => {
      action(["syncHighlightedItem"]);
    });
    track([() => prop("collection").toString()], () => {
      action(["syncHighlightedValue"]);
    });
  },
  effects: ["trackFocusVisible"],
  on: {
    "HIGHLIGHTED_VALUE.SET": {
      actions: ["setHighlightedItem"]
    },
    "ITEM.SELECT": {
      actions: ["selectItem"]
    },
    "ITEM.CLEAR": {
      actions: ["clearItem"]
    },
    "VALUE.SET": {
      actions: ["setSelectedItems"]
    },
    "VALUE.CLEAR": {
      actions: ["clearSelectedItems"]
    }
  },
  states: {
    idle: {
      effects: ["scrollToHighlightedItem"],
      on: {
        "INPUT.FOCUS": {
          actions: ["setFocused"]
        },
        "CONTENT.FOCUS": [
          {
            guard: or("hasSelectedValue", "hasHighlightedValue"),
            actions: ["setFocused"]
          },
          {
            actions: ["setFocused", "setDefaultHighlightedValue"]
          }
        ],
        "CONTENT.BLUR": {
          actions: ["clearFocused"]
        },
        "ITEM.CLICK": {
          actions: ["setHighlightedItem", "selectHighlightedItem"]
        },
        "CONTENT.TYPEAHEAD": {
          actions: ["setFocused", "highlightMatchingItem"]
        },
        "ITEM.POINTER_MOVE": {
          actions: ["highlightItem"]
        },
        "ITEM.POINTER_LEAVE": {
          actions: ["clearHighlightedItem"]
        },
        NAVIGATE: {
          actions: ["setFocused", "setHighlightedItem", "selectWithKeyboard"]
        }
      }
    }
  },
  implementations: {
    guards: {
      hasSelectedValue: ({ context }) => context.get("value").length > 0,
      hasHighlightedValue: ({ context }) => context.get("highlightedValue") != null
    },
    effects: {
      trackFocusVisible: ({ scope, refs }) => {
        return focusVisible.trackFocusVisible({
          root: scope.getRootNode?.(),
          onChange(details) {
            refs.set("focusVisible", details.isFocusVisible);
          }
        });
      },
      scrollToHighlightedItem({ context, prop, scope }) {
        const exec = (immediate) => {
          const highlightedValue = context.get("highlightedValue");
          if (highlightedValue == null) return;
          const modality = focusVisible.getInteractionModality();
          if (modality !== "keyboard") return;
          const contentEl2 = getContentEl(scope);
          const scrollToIndexFn = prop("scrollToIndexFn");
          if (scrollToIndexFn) {
            const highlightedIndex = prop("collection").indexOf(highlightedValue);
            scrollToIndexFn?.({
              index: highlightedIndex,
              immediate,
              getElement() {
                return getItemEl(scope, highlightedValue);
              }
            });
            return;
          }
          const itemEl = getItemEl(scope, highlightedValue);
          domQuery.scrollIntoView(itemEl, { rootEl: contentEl2, block: "nearest" });
        };
        domQuery.raf(() => exec(true));
        const contentEl = () => getContentEl(scope);
        return domQuery.observeAttributes(contentEl, {
          defer: true,
          attributes: ["data-activedescendant"],
          callback() {
            exec(false);
          }
        });
      }
    },
    actions: {
      selectHighlightedItem({ context, prop, event, computed }) {
        const value = event.value ?? context.get("highlightedValue");
        const collection2 = prop("collection");
        if (value == null || !collection2.has(value)) return;
        const selection = computed("selection");
        if (event.shiftKey && computed("multiple") && event.anchorValue) {
          const next = selection.extendSelection(collection2, event.anchorValue, value);
          invokeOnSelect(selection, next, prop("onSelect"));
          context.set("value", Array.from(next));
        } else {
          const next = selection.select(collection2, value, event.metaKey);
          invokeOnSelect(selection, next, prop("onSelect"));
          context.set("value", Array.from(next));
        }
      },
      selectWithKeyboard({ context, prop, event, computed }) {
        const selection = computed("selection");
        const collection2 = prop("collection");
        if (event.shiftKey && computed("multiple") && event.anchorValue) {
          const next = selection.extendSelection(collection2, event.anchorValue, event.value);
          invokeOnSelect(selection, next, prop("onSelect"));
          context.set("value", Array.from(next));
          return;
        }
        if (prop("selectOnHighlight")) {
          const next = selection.replaceSelection(collection2, event.value);
          invokeOnSelect(selection, next, prop("onSelect"));
          context.set("value", Array.from(next));
        }
      },
      highlightItem({ context, event }) {
        context.set("highlightedValue", event.value);
      },
      highlightMatchingItem({ context, prop, event, refs }) {
        const value = prop("collection").search(event.key, {
          state: refs.get("typeahead"),
          currentValue: context.get("highlightedValue")
        });
        if (value == null) return;
        context.set("highlightedValue", value);
      },
      setHighlightedItem({ context, event }) {
        context.set("highlightedValue", event.value);
      },
      clearHighlightedItem({ context }) {
        context.set("highlightedValue", null);
      },
      selectItem({ context, prop, event, computed }) {
        const collection2 = prop("collection");
        const selection = computed("selection");
        const next = selection.select(collection2, event.value);
        invokeOnSelect(selection, next, prop("onSelect"));
        context.set("value", Array.from(next));
      },
      clearItem({ context, event, computed }) {
        const selection = computed("selection");
        const value = selection.deselect(event.value);
        context.set("value", Array.from(value));
      },
      setSelectedItems({ context, event }) {
        context.set("value", event.value);
      },
      clearSelectedItems({ context }) {
        context.set("value", []);
      },
      syncSelectedItems({ context, prop }) {
        const collection2 = prop("collection");
        const prevSelectedItems = context.get("selectedItems");
        const value = context.get("value");
        const selectedItems = value.map((value2) => {
          const item = prevSelectedItems.find((item2) => collection2.getItemValue(item2) === value2);
          return item || collection2.find(value2);
        });
        context.set("selectedItems", selectedItems);
      },
      syncHighlightedItem({ context, prop }) {
        const collection2 = prop("collection");
        const highlightedValue = context.get("highlightedValue");
        const highlightedItem = highlightedValue ? collection2.find(highlightedValue) : null;
        context.set("highlightedItem", highlightedItem);
      },
      syncHighlightedValue({ context, prop }) {
        const collection2 = prop("collection");
        const highlightedValue = context.get("highlightedValue");
        if (highlightedValue != null && !collection2.has(highlightedValue)) {
          context.set("highlightedValue", null);
        }
      },
      setFocused({ context }) {
        context.set("focused", true);
      },
      setDefaultHighlightedValue({ context, prop }) {
        const collection2 = prop("collection");
        const firstValue = collection2.firstValue;
        if (firstValue != null) {
          context.set("highlightedValue", firstValue);
        }
      },
      clearFocused({ context }) {
        context.set("focused", false);
      }
    }
  }
});
var diff = (a, b) => {
  const result = new Set(a);
  for (const item of b) result.delete(item);
  return result;
};
function invokeOnSelect(current, next, onSelect) {
  const added = diff(next, current);
  for (const item of added) {
    onSelect?.({ value: item });
  }
}
var props = types.createProps()([
  "collection",
  "defaultHighlightedValue",
  "defaultValue",
  "dir",
  "disabled",
  "deselectable",
  "disallowSelectAll",
  "getRootNode",
  "highlightedValue",
  "id",
  "ids",
  "loopFocus",
  "onHighlightChange",
  "onSelect",
  "onValueChange",
  "orientation",
  "scrollToIndexFn",
  "selectionMode",
  "selectOnHighlight",
  "typeahead",
  "value"
]);
var splitProps = utils.createSplitProps(props);
var itemProps = types.createProps()(["item", "highlightOnHover"]);
var splitItemProps = utils.createSplitProps(itemProps);
var itemGroupProps = types.createProps()(["id"]);
var splitItemGroupProps = utils.createSplitProps(itemGroupProps);
var itemGroupLabelProps = types.createProps()(["htmlFor"]);
var splitItemGroupLabelProps = utils.createSplitProps(itemGroupLabelProps);
exports.anatomy = anatomy;
exports.collection = collection;
exports.connect = connect;
exports.gridCollection = gridCollection;
exports.itemGroupLabelProps = itemGroupLabelProps;
exports.itemGroupProps = itemGroupProps;
exports.itemProps = itemProps;
exports.machine = machine;
exports.props = props;
exports.splitItemGroupLabelProps = splitItemGroupLabelProps;
exports.splitItemGroupProps = splitItemGroupProps;
exports.splitItemProps = splitItemProps;
exports.splitProps = splitProps;