Packages
corex
0.1.1
0.1.2
0.1.1
0.1.0
0.1.0-rc.1
0.1.0-rc.0
0.1.0-beta.5
0.1.0-beta.4
0.1.0-beta.3
0.1.0-beta.2
0.1.0-beta.1
0.1.0-alpha.33
0.1.0-alpha.32
0.1.0-alpha.31
0.1.0-alpha.30
0.1.0-alpha.29
0.1.0-alpha.28
0.1.0-alpha.27
0.1.0-alpha.26
0.1.0-alpha.25
0.1.0-alpha.24
0.1.0-alpha.23
0.1.0-alpha.22
0.1.0-alpha.21
0.1.0-alpha.20
0.1.0-alpha.19
0.1.0-alpha.18
0.1.0-alpha.17
0.1.0-alpha.16
0.1.0-alpha.15
0.1.0-alpha.14
0.1.0-alpha.13
0.1.0-alpha.12
0.1.0-alpha.11
0.1.0-alpha.10
0.1.0-alpha.9
0.1.0-alpha.8
0.1.0-alpha.7
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
Accessible and unstyled UI components library written in Elixir and TypeScript that integrates Zag.js state machines into the Phoenix Framework.
Current section
Files
Jump to
Current section
Files
priv/static/toggle-group.mjs
import {
readStringListControlledZagUpdate
} from "./chunks/chunk-I2HPUDHJ.mjs";
import {
createDomEventRegistry,
createHookHandleEventRegistry
} from "./chunks/chunk-77HPO22C.mjs";
import {
idMatches,
notifyChange,
readPayloadId
} from "./chunks/chunk-2WCNJX5P.mjs";
import {
Component,
VanillaMachine,
addOrRemove,
canPushEvent,
contains,
createAnatomy,
createGuards,
createMachine,
dataAttr,
ensureProps,
first,
getBoolean,
getDir,
getEventKey,
getEventTarget,
getString,
getStringList,
isArray,
isEqual,
isSafari,
last,
nextById,
prevById,
queryAll,
raf
} from "./chunks/chunk-2GQRP3FN.mjs";
// ../node_modules/.pnpm/@zag-js+toggle-group@1.40.0/node_modules/@zag-js/toggle-group/dist/toggle-group.anatomy.mjs
var anatomy = createAnatomy("toggle-group").parts("root", "item");
var parts = anatomy.build();
// ../node_modules/.pnpm/@zag-js+toggle-group@1.40.0/node_modules/@zag-js/toggle-group/dist/toggle-group.dom.mjs
var getRootId = (ctx) => ctx.ids?.root ?? `toggle-group:${ctx.id}`;
var getItemId = (ctx, value) => ctx.ids?.item?.(value) ?? `toggle-group:${ctx.id}:${value}`;
var getRootEl = (ctx) => ctx.getById(getRootId(ctx));
var getElements = (ctx) => {
const ownerId = CSS.escape(getRootId(ctx));
const selector = `[data-ownedby='${ownerId}']:not([data-disabled])`;
return queryAll(getRootEl(ctx), selector);
};
var getFirstEl = (ctx) => first(getElements(ctx));
var getLastEl = (ctx) => last(getElements(ctx));
var getNextEl = (ctx, id, loopFocus) => nextById(getElements(ctx), id, loopFocus);
var getPrevEl = (ctx, id, loopFocus) => prevById(getElements(ctx), id, loopFocus);
// ../node_modules/.pnpm/@zag-js+toggle-group@1.40.0/node_modules/@zag-js/toggle-group/dist/toggle-group.connect.mjs
function connect(service, normalize) {
const { context, send, prop, scope } = service;
const value = context.get("value");
const disabled = prop("disabled");
const isSingle = !prop("multiple");
const rovingFocus = prop("rovingFocus");
const isHorizontal = prop("orientation") === "horizontal";
function getItemState(props) {
const id = getItemId(scope, props.value);
return {
id,
disabled: Boolean(props.disabled || disabled),
pressed: !!value.includes(props.value),
focused: context.get("focusedId") === id
};
}
return {
value,
setValue(value2) {
send({ type: "VALUE.SET", value: value2 });
},
getRootProps() {
return normalize.element({
...parts.root.attrs,
id: getRootId(scope),
dir: prop("dir"),
role: isSingle ? "radiogroup" : "group",
tabIndex: context.get("isTabbingBackward") ? -1 : 0,
"data-disabled": dataAttr(disabled),
"data-orientation": prop("orientation"),
"data-focus": dataAttr(context.get("focusedId") != null),
style: { outline: "none" },
onMouseDown() {
if (disabled) return;
send({ type: "ROOT.MOUSE_DOWN" });
},
onFocus(event) {
if (disabled) return;
if (event.currentTarget !== getEventTarget(event)) return;
if (context.get("isClickFocus")) return;
if (context.get("isTabbingBackward")) return;
send({ type: "ROOT.FOCUS" });
},
onBlur(event) {
const target = event.relatedTarget;
if (contains(event.currentTarget, target)) return;
if (disabled) return;
send({ type: "ROOT.BLUR" });
}
});
},
getItemState,
getItemProps(props) {
const itemState = getItemState(props);
const rovingTabIndex = itemState.focused ? 0 : -1;
return normalize.button({
...parts.item.attrs,
id: itemState.id,
type: "button",
"data-ownedby": getRootId(scope),
"data-focus": dataAttr(itemState.focused),
disabled: itemState.disabled,
tabIndex: rovingFocus ? rovingTabIndex : void 0,
// radio
role: isSingle ? "radio" : void 0,
"aria-checked": isSingle ? itemState.pressed : void 0,
"aria-pressed": isSingle ? void 0 : itemState.pressed,
//
"data-disabled": dataAttr(itemState.disabled),
"data-orientation": prop("orientation"),
dir: prop("dir"),
"data-state": itemState.pressed ? "on" : "off",
onFocus() {
if (itemState.disabled) return;
send({ type: "TOGGLE.FOCUS", id: itemState.id });
},
onClick(event) {
if (itemState.disabled) return;
send({ type: "TOGGLE.CLICK", id: itemState.id, value: props.value });
if (isSafari()) {
event.currentTarget.focus({ preventScroll: true });
}
},
onKeyDown(event) {
if (event.defaultPrevented) return;
if (!contains(event.currentTarget, getEventTarget(event))) return;
if (itemState.disabled) return;
const keyMap = {
Tab(event2) {
const isShiftTab = event2.shiftKey;
send({ type: "TOGGLE.SHIFT_TAB", isShiftTab });
},
ArrowLeft() {
if (!rovingFocus || !isHorizontal) return;
send({ type: "TOGGLE.FOCUS_PREV" });
},
ArrowRight() {
if (!rovingFocus || !isHorizontal) return;
send({ type: "TOGGLE.FOCUS_NEXT" });
},
ArrowUp() {
if (!rovingFocus || isHorizontal) return;
send({ type: "TOGGLE.FOCUS_PREV" });
},
ArrowDown() {
if (!rovingFocus || isHorizontal) return;
send({ type: "TOGGLE.FOCUS_NEXT" });
},
Home() {
if (!rovingFocus) return;
send({ type: "TOGGLE.FOCUS_FIRST" });
},
End() {
if (!rovingFocus) return;
send({ type: "TOGGLE.FOCUS_LAST" });
}
};
const exec = keyMap[getEventKey(event)];
if (exec) {
exec(event);
if (event.key !== "Tab") event.preventDefault();
}
}
});
}
};
}
// ../node_modules/.pnpm/@zag-js+toggle-group@1.40.0/node_modules/@zag-js/toggle-group/dist/toggle-group.machine.mjs
var { not, and } = createGuards();
var machine = createMachine({
props({ props }) {
return {
defaultValue: [],
orientation: "horizontal",
rovingFocus: true,
loopFocus: true,
deselectable: true,
...props
};
},
initialState() {
return "idle";
},
context({ prop, bindable }) {
return {
value: bindable(() => ({
defaultValue: prop("defaultValue"),
value: prop("value"),
onChange(value) {
prop("onValueChange")?.({ value });
}
})),
focusedId: bindable(() => ({
defaultValue: null
})),
isTabbingBackward: bindable(() => ({
defaultValue: false
})),
isClickFocus: bindable(() => ({
defaultValue: false
})),
isWithinToolbar: bindable(() => ({
defaultValue: false
}))
};
},
computed: {
currentLoopFocus: ({ context, prop }) => prop("loopFocus") && !context.get("isWithinToolbar")
},
entry: ["checkIfWithinToolbar"],
on: {
"VALUE.SET": {
actions: ["setValue"]
},
"TOGGLE.CLICK": {
actions: ["setValue"]
},
"ROOT.MOUSE_DOWN": {
actions: ["setClickFocus"]
}
},
states: {
idle: {
on: {
"ROOT.FOCUS": {
target: "focused",
guard: not(and("isClickFocus", "isTabbingBackward")),
actions: ["focusFirstToggle", "clearClickFocus"]
},
"TOGGLE.FOCUS": {
target: "focused",
actions: ["setFocusedId"]
}
}
},
focused: {
on: {
"ROOT.BLUR": {
target: "idle",
actions: ["clearIsTabbingBackward", "clearFocusedId", "clearClickFocus"]
},
"TOGGLE.FOCUS": {
actions: ["setFocusedId"]
},
"TOGGLE.FOCUS_NEXT": {
actions: ["focusNextToggle"]
},
"TOGGLE.FOCUS_PREV": {
actions: ["focusPrevToggle"]
},
"TOGGLE.FOCUS_FIRST": {
actions: ["focusFirstToggle"]
},
"TOGGLE.FOCUS_LAST": {
actions: ["focusLastToggle"]
},
"TOGGLE.SHIFT_TAB": [
{
guard: not("isFirstToggleFocused"),
target: "idle",
actions: ["setIsTabbingBackward"]
},
{
actions: ["setIsTabbingBackward"]
}
]
}
}
},
implementations: {
guards: {
isClickFocus: ({ context }) => context.get("isClickFocus"),
isTabbingBackward: ({ context }) => context.get("isTabbingBackward"),
isFirstToggleFocused: ({ context, scope }) => context.get("focusedId") === getFirstEl(scope)?.id
},
actions: {
setIsTabbingBackward({ context }) {
context.set("isTabbingBackward", true);
},
clearIsTabbingBackward({ context }) {
context.set("isTabbingBackward", false);
},
setClickFocus({ context }) {
context.set("isClickFocus", true);
},
clearClickFocus({ context }) {
context.set("isClickFocus", false);
},
checkIfWithinToolbar({ context, scope }) {
const closestToolbar = getRootEl(scope)?.closest("[role=toolbar]");
context.set("isWithinToolbar", !!closestToolbar);
},
setFocusedId({ context, event }) {
context.set("focusedId", event.id);
},
clearFocusedId({ context }) {
context.set("focusedId", null);
},
setValue({ context, event, prop }) {
ensureProps(event, ["value"]);
let next = context.get("value");
if (isArray(event.value)) {
next = event.value;
} else if (prop("multiple")) {
next = addOrRemove(next, event.value);
} else {
const isSelected = isEqual(next, [event.value]);
next = isSelected && prop("deselectable") ? [] : [event.value];
}
context.set("value", next);
},
focusNextToggle({ context, scope, prop }) {
raf(() => {
const focusedId = context.get("focusedId");
if (!focusedId) return;
getNextEl(scope, focusedId, prop("loopFocus"))?.focus({ preventScroll: true });
});
},
focusPrevToggle({ context, scope, prop }) {
raf(() => {
const focusedId = context.get("focusedId");
if (!focusedId) return;
getPrevEl(scope, focusedId, prop("loopFocus"))?.focus({ preventScroll: true });
});
},
focusFirstToggle({ scope }) {
raf(() => {
getFirstEl(scope)?.focus({ preventScroll: true });
});
},
focusLastToggle({ scope }) {
raf(() => {
getLastEl(scope)?.focus({ preventScroll: true });
});
}
}
}
});
// components/toggle-group.ts
var ToggleGroup = class extends Component {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
initMachine(props) {
return new VanillaMachine(machine, props);
}
initApi() {
return this.zagConnect(connect);
}
render() {
const rootEl = this.el.querySelector(
'[data-scope="toggle-group"][data-part="root"]'
);
if (!rootEl) return;
this.spreadProps(rootEl, this.api.getRootProps());
const items = this.el.querySelectorAll(
'[data-scope="toggle-group"][data-part="item"]'
);
for (let i = 0; i < items.length; i++) {
const itemEl = items[i];
const value = getString(itemEl, "value");
if (!value) continue;
const disabled = getBoolean(itemEl, "disabled");
this.spreadProps(itemEl, this.api.getItemProps({ value, disabled }));
}
}
};
// hooks/toggle-group.ts
function valueChangePayload(el, details) {
return {
id: el.id,
value: details.value
};
}
function readToggleGroupPayloadValue(payload) {
if (!payload || typeof payload !== "object") return void 0;
const o = payload;
const v = o.value ?? o["value"];
if (Array.isArray(v) && v.every((x) => typeof x === "string")) return v;
return void 0;
}
var ToggleGroupHook = {
mounted() {
const el = this.el;
const pushEvent = this.pushEvent.bind(this);
const canPush = () => canPushEvent(this.liveSocket);
const props = {
id: el.id,
...getBoolean(el, "controlled") ? { value: getStringList(el, "value") } : { defaultValue: getStringList(el, "defaultValue") },
deselectable: getBoolean(el, "deselectable"),
loopFocus: getBoolean(el, "loopFocus"),
rovingFocus: getBoolean(el, "rovingFocus"),
disabled: getBoolean(el, "disabled"),
multiple: getBoolean(el, "multiple"),
orientation: getString(el, "orientation"),
dir: getDir(el),
onValueChange: (details) => {
notifyChange({
el,
canPushServer: canPush(),
pushEvent,
payload: valueChangePayload(el, details),
serverEventName: getString(el, "onValueChange"),
clientEventName: getString(el, "onValueChangeClient")
});
}
};
const toggleGroup = new ToggleGroup(el, props);
toggleGroup.init();
this.toggleGroup = toggleGroup;
const domRegistry = createDomEventRegistry(el);
this.domRegistry = domRegistry;
domRegistry.add("corex:toggle-group:set-value", (event) => {
const { value } = event.detail;
toggleGroup.api.setValue(value);
});
const registry = createHookHandleEventRegistry(this);
this.handleRegistry = registry;
registry.add("toggle-group_set_value", (payload) => {
if (!idMatches(el.id, readPayloadId(payload))) return;
const value = readToggleGroupPayloadValue(payload);
if (value) toggleGroup.api.setValue(value);
});
registry.add("toggle-group:value", (payload) => {
if (!idMatches(el.id, readPayloadId(payload))) return;
if (!canPush()) return;
this.pushEvent("toggle-group:value_response", {
id: el.id,
value: toggleGroup.api.value
});
});
},
updated() {
this.toggleGroup?.updateProps({
...readStringListControlledZagUpdate(this.el, "value", "defaultValue"),
deselectable: getBoolean(this.el, "deselectable"),
loopFocus: getBoolean(this.el, "loopFocus"),
rovingFocus: getBoolean(this.el, "rovingFocus"),
disabled: getBoolean(this.el, "disabled"),
multiple: getBoolean(this.el, "multiple"),
orientation: getString(this.el, "orientation"),
dir: getDir(this.el)
});
},
destroyed() {
this.domRegistry?.teardown();
this.handleRegistry?.teardown();
this.toggleGroup?.destroy();
}
};
export {
ToggleGroupHook as ToggleGroup,
readToggleGroupPayloadValue,
valueChangePayload
};