@patchbayhq/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adsr.js","sourceRoot":"","sources":["../../src/components/adsr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type MenuChangeDetail = {
2
+ label: string;
3
+ source: "user";
4
+ value: string;
5
+ };
6
+ export declare function initMenus(root?: ParentNode): void;
7
+ //# sourceMappingURL=menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/components/menu.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAIF,wBAAgB,SAAS,CAAC,IAAI,GAAE,UAAqB,GAAG,IAAI,CAO3D"}
@@ -0,0 +1,136 @@
1
+ const controllerKey = "__menuController";
2
+ export function initMenus(root = document) {
3
+ root.querySelectorAll("[data-menu]").forEach((element) => {
4
+ if (!element[controllerKey]) {
5
+ element[controllerKey] = new MenuController(element);
6
+ }
7
+ element[controllerKey]?.sync();
8
+ });
9
+ }
10
+ class MenuController {
11
+ root;
12
+ activeIndex = 0;
13
+ button;
14
+ list;
15
+ constructor(root) {
16
+ this.root = root;
17
+ this.button = root.querySelector(".menu__button");
18
+ this.list = root.querySelector(".menu__list");
19
+ this.button?.addEventListener("click", () => this.toggle());
20
+ this.button?.addEventListener("keydown", (event) => this.handleKeydown(event));
21
+ this.options.forEach((option, index) => {
22
+ option.addEventListener("click", () => this.commit(index));
23
+ option.addEventListener("mouseenter", () => {
24
+ this.activeIndex = index;
25
+ this.sync();
26
+ });
27
+ });
28
+ this.root.addEventListener("focusout", (event) => {
29
+ if (event.relatedTarget instanceof Node &&
30
+ this.root.contains(event.relatedTarget)) {
31
+ return;
32
+ }
33
+ this.setOpen(false);
34
+ });
35
+ this.sync();
36
+ }
37
+ get options() {
38
+ return Array.from(this.root.querySelectorAll(".menu__option"));
39
+ }
40
+ sync() {
41
+ const options = this.options;
42
+ const selectedIndex = Math.max(0, options.findIndex((option) => option.dataset.value === this.value));
43
+ const selected = options[selectedIndex] ?? options[0];
44
+ this.activeIndex = Math.min(this.activeIndex, Math.max(options.length - 1, 0));
45
+ if (selected && !this.root.dataset.value) {
46
+ this.root.dataset.value = selected.dataset.value ?? selected.textContent ?? "";
47
+ }
48
+ if (this.button) {
49
+ const value = this.button.querySelector(".menu__value");
50
+ if (value && selected) {
51
+ value.textContent = selected.textContent ?? "";
52
+ }
53
+ this.button.setAttribute("aria-expanded", String(this.open));
54
+ }
55
+ if (this.list) {
56
+ this.list.hidden = !this.open;
57
+ this.list.setAttribute("aria-activedescendant", options[this.activeIndex]?.id ?? "");
58
+ }
59
+ options.forEach((option, index) => {
60
+ const selectedOption = option === selected;
61
+ option.classList.toggle("is-active", index === this.activeIndex);
62
+ option.classList.toggle("is-selected", selectedOption);
63
+ option.setAttribute("aria-selected", String(selectedOption));
64
+ option.id ||= `${this.list?.id ?? "menu"}-option-${index}`;
65
+ if (!option.dataset.value) {
66
+ option.dataset.value = option.textContent ?? "";
67
+ }
68
+ });
69
+ }
70
+ get open() {
71
+ return this.root.dataset.open === "true";
72
+ }
73
+ get value() {
74
+ return this.root.dataset.value ?? "";
75
+ }
76
+ setOpen(open) {
77
+ if (open) {
78
+ this.root.dataset.open = "true";
79
+ }
80
+ else {
81
+ delete this.root.dataset.open;
82
+ }
83
+ this.sync();
84
+ }
85
+ toggle() {
86
+ this.setOpen(!this.open);
87
+ }
88
+ firstEnabledIndex(startIndex, direction) {
89
+ const options = this.options;
90
+ for (let offset = 0; offset < options.length; offset += 1) {
91
+ const index = (startIndex + offset * direction + options.length) % options.length;
92
+ if (!options[index]?.disabled)
93
+ return index;
94
+ }
95
+ return this.activeIndex;
96
+ }
97
+ handleKeydown(event) {
98
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
99
+ event.preventDefault();
100
+ const direction = event.key === "ArrowDown" ? 1 : -1;
101
+ this.activeIndex = this.firstEnabledIndex(this.activeIndex + direction, direction);
102
+ this.setOpen(true);
103
+ return;
104
+ }
105
+ if (event.key === "Enter" || event.key === " ") {
106
+ event.preventDefault();
107
+ if (this.open) {
108
+ this.commit(this.activeIndex);
109
+ }
110
+ else {
111
+ this.setOpen(true);
112
+ }
113
+ return;
114
+ }
115
+ if (event.key === "Escape") {
116
+ this.setOpen(false);
117
+ }
118
+ }
119
+ commit(index) {
120
+ const option = this.options[index];
121
+ if (!option || option.disabled)
122
+ return;
123
+ this.root.dataset.value = option.dataset.value ?? option.textContent ?? "";
124
+ this.activeIndex = index;
125
+ this.setOpen(false);
126
+ this.root.dispatchEvent(new CustomEvent("menu-change", {
127
+ bubbles: true,
128
+ detail: {
129
+ label: option.textContent ?? "",
130
+ source: "user",
131
+ value: this.root.dataset.value,
132
+ },
133
+ }));
134
+ }
135
+ }
136
+ //# sourceMappingURL=menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/components/menu.ts"],"names":[],"mappings":"AAgBA,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAEzC,MAAM,UAAU,SAAS,CAAC,OAAmB,QAAQ;IACnD,IAAI,CAAC,gBAAgB,CAAW,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACjE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,cAAc;IAKW;IAJrB,WAAW,GAAG,CAAC,CAAC;IACP,MAAM,CAA2B;IACjC,IAAI,CAAqB;IAE1C,YAA6B,IAAc;QAAd,SAAI,GAAJ,IAAI,CAAU;QACzC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAoB,eAAe,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa,CAAc,aAAa,CAAC,CAAC;QAE3D,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CACjD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAC1B,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACrC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3D,MAAM,CAAC,gBAAgB,CAAC,YAAY,EAAE,GAAG,EAAE;gBACzC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;gBACzB,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/C,IACE,KAAK,CAAC,aAAa,YAAY,IAAI;gBACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,EACvC,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,IAAI,OAAO;QACT,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAa,eAAe,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAC5B,CAAC,EACD,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,CACnE,CAAC;QACF,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;QAEtD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAE/E,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;QACjF,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAc,cAAc,CAAC,CAAC;YACrE,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;gBACtB,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;YACjD,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,YAAY,CACpB,uBAAuB,EACvB,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,CACpC,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YAChC,MAAM,cAAc,GAAG,MAAM,KAAK,QAAQ,CAAC;YAE3C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,KAAK,IAAI,CAAC,WAAW,CAAC,CAAC;YACjE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;YACvD,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;YAC7D,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,MAAM,WAAW,KAAK,EAAE,CAAC;YAC3D,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC1B,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;YAClD,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAY,IAAI;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC;IAC3C,CAAC;IAED,IAAY,KAAK;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IACvC,CAAC;IAEO,OAAO,CAAC,IAAa;QAC3B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAEO,iBAAiB,CAAC,UAAkB,EAAE,SAAiB;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAE7B,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC;YAC1D,MAAM,KAAK,GACT,CAAC,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;YACtE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ;gBAAE,OAAO,KAAK,CAAC;QAC9C,CAAC;QAED,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAEO,aAAa,CAAC,KAAoB;QACxC,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YACzD,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,iBAAiB,CACvC,IAAI,CAAC,WAAW,GAAG,SAAS,EAC5B,SAAS,CACV,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YAC/C,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,KAAa;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO;QAEvC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;QAC3E,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,aAAa,CACrB,IAAI,WAAW,CAAmB,aAAa,EAAE;YAC/C,OAAO,EAAE,IAAI;YACb,MAAM,EAAE;gBACN,KAAK,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;gBAC/B,MAAM,EAAE,MAAM;gBACd,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK;aAC/B;SACF,CAAC,CACH,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export declare function defineSequencerElements(): void;
2
+ //# sourceMappingURL=sequencers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequencers.d.ts","sourceRoot":"","sources":["../../src/components/sequencers.ts"],"names":[],"mappings":"AA4BA,wBAAgB,uBAAuB,IAAI,IAAI,CAQ9C"}
@@ -0,0 +1,477 @@
1
+ import { createGridState, createStepKeyChange, createStepSequencerState, cycleGridDirection, gridRowFromY, stepKeys, stepLaneCount, stepNoteAccent, stepStepCount, stepStepsPerBar, setStepLoopEnd, setStepLoopStart, toggleGridCell, toggleStepNote, } from "../sequencers";
2
+ export function defineSequencerElements() {
3
+ if (!customElements.get("sequencer-grid")) {
4
+ customElements.define("sequencer-grid", GridElement);
5
+ }
6
+ if (!customElements.get("step-sequencer")) {
7
+ customElements.define("step-sequencer", StepElement);
8
+ }
9
+ }
10
+ class GridElement extends HTMLElement {
11
+ static observedAttributes = ["cells", "directions", "measure-size"];
12
+ state = createGridState();
13
+ get cells() {
14
+ return this.state.cells.map((cell) => ({ ...cell }));
15
+ }
16
+ set cells(cells) {
17
+ this.updateState({ cells });
18
+ }
19
+ get directions() {
20
+ return [...this.state.directions];
21
+ }
22
+ set directions(directions) {
23
+ this.updateState({ directions });
24
+ }
25
+ get measureSize() {
26
+ return this.state.measureSize;
27
+ }
28
+ set measureSize(measureSize) {
29
+ this.updateState({ measureSize });
30
+ }
31
+ connectedCallback() {
32
+ this.classList.add("grid");
33
+ this.setAttribute("role", this.getAttribute("role") ?? "group");
34
+ this.render();
35
+ }
36
+ attributeChangedCallback() {
37
+ this.syncStateFromAttributes();
38
+ if (this.isConnected) {
39
+ this.render();
40
+ }
41
+ }
42
+ updateState(next) {
43
+ this.state = createGridState({ ...this.state, ...next });
44
+ if (this.isConnected) {
45
+ this.render();
46
+ }
47
+ }
48
+ syncStateFromAttributes() {
49
+ this.state = createGridState({
50
+ ...this.state,
51
+ cells: jsonAttribute(this, "cells") ?? this.state.cells,
52
+ directions: jsonAttribute(this, "directions") ??
53
+ this.state.directions,
54
+ measureSize: numberAttribute(this, "measure-size") ?? this.state.measureSize,
55
+ });
56
+ }
57
+ render() {
58
+ this.style.setProperty("--grid-columns", String(this.state.cells.length));
59
+ this.replaceChildren();
60
+ this.state.cells.forEach((cell, index) => {
61
+ const button = document.createElement("button");
62
+ const column = index + 1;
63
+ const row = gridRowFromY(cell.y);
64
+ button.type = "button";
65
+ button.className = cell.active
66
+ ? "grid__cell is-active"
67
+ : "grid__cell";
68
+ button.dataset.gridCell = String(column);
69
+ button.dataset.gridColumn = String(column);
70
+ button.dataset.gridRole = "cell";
71
+ button.dataset.gridState = cell.active ? "active" : "inactive";
72
+ button.dataset.gridY = cell.y.toFixed(4);
73
+ button.style.setProperty("--grid-marker-y", `${cell.y * 100}%`);
74
+ button.setAttribute("aria-pressed", String(cell.active));
75
+ button.setAttribute("aria-label", cell.active
76
+ ? `Grid column ${column}, row ${row}`
77
+ : `Grid column ${column}, inactive`);
78
+ button.addEventListener("pointerdown", (event) => this.handleCellPointer(event, column));
79
+ button.addEventListener("keydown", (event) => this.handleCellKeydown(event, column, cell.y));
80
+ this.append(button);
81
+ });
82
+ this.state.directions.forEach((direction, index) => {
83
+ const button = document.createElement("button");
84
+ const column = index + 1;
85
+ button.type = "button";
86
+ button.className = `grid__direction is-direction-${direction}`;
87
+ button.dataset.gridDirection = direction;
88
+ button.dataset.gridDirectionControl = String(column);
89
+ button.dataset.gridDirectionValue = gridDirectionValue(direction);
90
+ button.dataset.gridRole = "direction";
91
+ button.setAttribute("aria-label", `Grid direction ${column}: ${direction}`);
92
+ button.title = direction;
93
+ button.addEventListener("click", () => this.handleDirectionClick(column));
94
+ this.append(button);
95
+ });
96
+ }
97
+ handleCellPointer(event, column) {
98
+ if (event.button !== 0)
99
+ return;
100
+ event.preventDefault();
101
+ const button = event.currentTarget;
102
+ const rect = button.getBoundingClientRect();
103
+ const y = rect.height > 0
104
+ ? (event.clientY - rect.top) / rect.height
105
+ : (this.state.cells[column - 1]?.y ?? 0.5);
106
+ const { change, state } = toggleGridCell(this.state, column, y);
107
+ this.state = state;
108
+ this.render();
109
+ this.dispatchGridChange(change);
110
+ }
111
+ handleCellKeydown(event, column, y) {
112
+ if (event.key !== "Enter" && event.key !== " ")
113
+ return;
114
+ event.preventDefault();
115
+ const { change, state } = toggleGridCell(this.state, column, y);
116
+ this.state = state;
117
+ this.render();
118
+ this.dispatchGridChange(change);
119
+ }
120
+ handleDirectionClick(column) {
121
+ const { change, state } = cycleGridDirection(this.state, column);
122
+ this.state = state;
123
+ this.render();
124
+ this.dispatchGridChange(change);
125
+ }
126
+ dispatchGridChange(detail) {
127
+ this.dispatchEvent(new CustomEvent("grid-change", {
128
+ bubbles: true,
129
+ composed: true,
130
+ detail,
131
+ }));
132
+ }
133
+ }
134
+ class StepElement extends HTMLElement {
135
+ static observedAttributes = ["active-key", "bars", "loop", "notes"];
136
+ keyPressAbortController;
137
+ loopDragAbortController;
138
+ pressedKey = null;
139
+ state = createStepSequencerState();
140
+ get activeKey() {
141
+ return this.state.activeKey;
142
+ }
143
+ set activeKey(activeKey) {
144
+ this.updateState({ activeKey });
145
+ }
146
+ get notes() {
147
+ return this.state.notes.map((note) => ({ ...note }));
148
+ }
149
+ set notes(notes) {
150
+ this.updateState({ notes });
151
+ }
152
+ get loop() {
153
+ return { ...this.state.loop };
154
+ }
155
+ set loop(loop) {
156
+ this.updateState({ loop });
157
+ }
158
+ get bars() {
159
+ return this.state.bars;
160
+ }
161
+ set bars(bars) {
162
+ this.updateState({ bars });
163
+ }
164
+ connectedCallback() {
165
+ this.classList.add("step-sequencer");
166
+ this.setAttribute("role", this.getAttribute("role") ?? "group");
167
+ this.render();
168
+ }
169
+ disconnectedCallback() {
170
+ this.keyPressAbortController?.abort();
171
+ this.loopDragAbortController?.abort();
172
+ this.keyPressAbortController = undefined;
173
+ this.loopDragAbortController = undefined;
174
+ this.pressedKey = null;
175
+ }
176
+ attributeChangedCallback() {
177
+ this.syncStateFromAttributes();
178
+ if (this.isConnected) {
179
+ this.render();
180
+ }
181
+ }
182
+ updateState(next) {
183
+ this.state = createStepSequencerState({ ...this.state, ...next });
184
+ if (this.isConnected) {
185
+ this.render();
186
+ }
187
+ }
188
+ syncStateFromAttributes() {
189
+ this.state = createStepSequencerState({
190
+ ...this.state,
191
+ activeKey: this.getAttribute("active-key") ??
192
+ this.state.activeKey,
193
+ bars: numberAttribute(this, "bars") ?? this.state.bars,
194
+ loop: jsonAttribute(this, "loop") ?? this.state.loop,
195
+ notes: jsonAttribute(this, "notes") ?? this.state.notes,
196
+ });
197
+ }
198
+ render() {
199
+ this.replaceChildren();
200
+ const displayKey = this.pressedKey ?? this.state.activeKey;
201
+ this.dataset.activeKey = this.state.activeKey;
202
+ this.dataset.activeKeyIndex = String(stepKeys.indexOf(this.state.activeKey) + 1);
203
+ if (this.pressedKey) {
204
+ this.dataset.pressedKey = this.pressedKey;
205
+ this.dataset.pressedKeyIndex = String(stepKeys.indexOf(this.pressedKey) + 1);
206
+ }
207
+ else {
208
+ delete this.dataset.pressedKey;
209
+ delete this.dataset.pressedKeyIndex;
210
+ }
211
+ this.dataset.loopStart = String(this.state.loop.start);
212
+ this.dataset.loopEnd = String(this.state.loop.end);
213
+ this.dataset.loopLength = String(this.state.loop.end - this.state.loop.start + 1);
214
+ this.dataset.loopBar = String(Math.floor((this.state.loop.start - 1) / stepStepsPerBar) + 1);
215
+ this.setAttribute("aria-label", `Step sequencer, key ${displayKey}, loop steps ${this.state.loop.start} through ${this.state.loop.end}`);
216
+ this.append(this.renderLoopRuler(), this.renderLabels(), this.renderKeys(), this.renderRoll());
217
+ }
218
+ renderLabels() {
219
+ const labels = document.createElement("div");
220
+ const activeKey = document.createElement("span");
221
+ const displayKey = this.pressedKey ?? this.state.activeKey;
222
+ labels.className = "step-sequencer__labels";
223
+ activeKey.textContent = displayKey;
224
+ labels.append(activeKey);
225
+ return labels;
226
+ }
227
+ renderKeys() {
228
+ const keys = document.createElement("div");
229
+ keys.className = "step-sequencer__keys";
230
+ keys.setAttribute("role", "group");
231
+ keys.setAttribute("aria-label", "Pitch keys");
232
+ stepKeys.forEach((key, index) => {
233
+ const button = document.createElement("span");
234
+ const tone = key.includes("#") ? "black" : "white";
235
+ const pressed = key === this.pressedKey;
236
+ button.className = tone === "black" ? "is-black" : "is-white";
237
+ button.dataset.stepKey = String(index + 1);
238
+ button.dataset.stepKeyState = pressed ? "pressed" : "idle";
239
+ button.dataset.stepKeyTone = tone;
240
+ button.setAttribute("aria-label", `Key ${key}`);
241
+ button.setAttribute("aria-pressed", String(pressed));
242
+ button.setAttribute("role", "button");
243
+ button.setAttribute("tabindex", "0");
244
+ button.classList.toggle("is-active", pressed);
245
+ button.addEventListener("pointerdown", (event) => this.handleKeyPointerDown(event, key));
246
+ button.addEventListener("keydown", (event) => this.handleKeyKeydown(event, key));
247
+ button.addEventListener("keyup", (event) => this.handleKeyKeyup(event, key));
248
+ button.addEventListener("blur", () => this.releaseKey(key));
249
+ keys.append(button);
250
+ });
251
+ return keys;
252
+ }
253
+ renderRoll() {
254
+ const roll = document.createElement("div");
255
+ roll.className = "step-sequencer__roll";
256
+ roll.setAttribute("role", "grid");
257
+ roll.setAttribute("aria-label", "Step notes");
258
+ roll.setAttribute("aria-rowcount", String(stepLaneCount));
259
+ roll.setAttribute("aria-colcount", String(stepStepCount));
260
+ Array.from({ length: stepStepCount * stepLaneCount }, (_, index) => {
261
+ const step = (index % stepStepCount) + 1;
262
+ const lane = Math.floor(index / stepStepCount) + 1;
263
+ const note = this.state.notes.find((candidate) => candidate.step === step && candidate.lane === lane);
264
+ const accent = stepNoteAccent(step);
265
+ const button = document.createElement("button");
266
+ button.type = "button";
267
+ button.dataset.stepBarStart = String(accent === "blue");
268
+ button.setAttribute("aria-label", `Step ${step}, lane ${lane}`);
269
+ button.setAttribute("aria-colindex", String(step));
270
+ button.setAttribute("aria-rowindex", String(lane));
271
+ button.setAttribute("aria-pressed", String(Boolean(note)));
272
+ button.setAttribute("aria-selected", String(Boolean(note)));
273
+ button.setAttribute("role", "gridcell");
274
+ button.className = note ? "is-active" : "";
275
+ button.addEventListener("click", () => this.handleNoteToggle(step, lane));
276
+ if (note) {
277
+ const marker = document.createElement("span");
278
+ marker.className = ["step-sequencer__note", `is-${accent}`].join(" ");
279
+ marker.dataset.stepNoteAccent = accent;
280
+ button.append(marker);
281
+ }
282
+ roll.append(button);
283
+ });
284
+ return roll;
285
+ }
286
+ renderLoopRuler() {
287
+ const ruler = document.createElement("div");
288
+ const track = document.createElement("div");
289
+ const range = document.createElement("div");
290
+ const startHandle = this.renderLoopHandle("start");
291
+ const endHandle = this.renderLoopHandle("end");
292
+ ruler.className = "step-sequencer__loop-ruler";
293
+ ruler.dataset.stepLoopRuler = "true";
294
+ ruler.setAttribute("role", "group");
295
+ ruler.setAttribute("aria-label", "Loop selector");
296
+ track.className = "step-sequencer__loop-track";
297
+ track.dataset.stepLoopTrack = "true";
298
+ range.className = "step-sequencer__loop-range";
299
+ range.dataset.stepLoopRange = "true";
300
+ range.dataset.stepLoopStart = String(this.state.loop.start);
301
+ range.dataset.stepLoopEnd = String(this.state.loop.end);
302
+ range.dataset.stepLoopLength = String(this.state.loop.end - this.state.loop.start + 1);
303
+ range.style.gridColumn = `${this.state.loop.start} / ${this.state.loop.end + 1}`;
304
+ range.setAttribute("aria-label", `Loop steps ${this.state.loop.start} through ${this.state.loop.end}`);
305
+ range.append(startHandle, endHandle);
306
+ ruler.append(track, range);
307
+ return ruler;
308
+ }
309
+ renderLoopHandle(edge) {
310
+ const handle = document.createElement("button");
311
+ const arrow = document.createElement("span");
312
+ const label = document.createElement("span");
313
+ const step = edge === "start" ? this.state.loop.start : this.state.loop.end;
314
+ const direction = edge === "start" ? "right" : "left";
315
+ handle.type = "button";
316
+ handle.className = `step-sequencer__loop-handle is-${edge}`;
317
+ handle.dataset.stepLoopHandle = edge;
318
+ arrow.className = `step-sequencer__loop-arrow is-${direction}`;
319
+ arrow.dataset.stepLoopArrow = direction;
320
+ arrow.setAttribute("aria-hidden", "true");
321
+ label.className = "step-sequencer__loop-label";
322
+ label.textContent = String(step);
323
+ handle.append(...(edge === "start" ? [label, arrow] : [arrow, label]));
324
+ handle.setAttribute("aria-label", `${edge === "start" ? "Loop start" : "Loop end"} step ${step}`);
325
+ handle.addEventListener("pointerdown", (event) => this.handleLoopPointerDown(event, edge));
326
+ handle.addEventListener("keydown", (event) => this.handleLoopHandleKeydown(event, edge));
327
+ return handle;
328
+ }
329
+ handleKeyPointerDown(event, key) {
330
+ if (event.button !== 0)
331
+ return;
332
+ event.preventDefault();
333
+ this.pressKey(key);
334
+ this.watchPointerRelease(key);
335
+ }
336
+ handleKeyKeydown(event, key) {
337
+ if (event.key !== "Enter" && event.key !== " ")
338
+ return;
339
+ event.preventDefault();
340
+ if (!event.repeat) {
341
+ this.pressKey(key);
342
+ }
343
+ }
344
+ handleKeyKeyup(event, key) {
345
+ if (event.key !== "Enter" && event.key !== " ")
346
+ return;
347
+ event.preventDefault();
348
+ this.releaseKey(key);
349
+ }
350
+ pressKey(key) {
351
+ if (this.pressedKey === key)
352
+ return;
353
+ if (this.pressedKey) {
354
+ this.releaseKey();
355
+ }
356
+ this.pressedKey = key;
357
+ this.render();
358
+ this.dispatchStepChange(createStepKeyChange(this.state, key, true));
359
+ }
360
+ releaseKey(key = this.pressedKey) {
361
+ if (!this.pressedKey || (key && this.pressedKey !== key))
362
+ return;
363
+ const releasedKey = this.pressedKey;
364
+ this.keyPressAbortController?.abort();
365
+ this.keyPressAbortController = undefined;
366
+ this.pressedKey = null;
367
+ this.render();
368
+ this.dispatchStepChange(createStepKeyChange(this.state, releasedKey, false));
369
+ }
370
+ watchPointerRelease(key) {
371
+ this.keyPressAbortController?.abort();
372
+ this.keyPressAbortController = new AbortController();
373
+ window.addEventListener("pointerup", () => this.releaseKey(key), {
374
+ once: true,
375
+ signal: this.keyPressAbortController.signal,
376
+ });
377
+ window.addEventListener("pointercancel", () => this.releaseKey(key), {
378
+ once: true,
379
+ signal: this.keyPressAbortController.signal,
380
+ });
381
+ }
382
+ handleLoopPointerDown(event, edge) {
383
+ if (event.button !== 0)
384
+ return;
385
+ event.preventDefault();
386
+ this.updateLoopEdgeFromClientX(edge, event.clientX);
387
+ this.watchLoopDrag(edge);
388
+ }
389
+ handleLoopHandleKeydown(event, edge) {
390
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
391
+ return;
392
+ event.preventDefault();
393
+ const delta = event.key === "ArrowLeft" ? -1 : 1;
394
+ const step = edge === "start"
395
+ ? this.state.loop.start + delta
396
+ : this.state.loop.end + delta;
397
+ this.updateLoopEdge(edge, step);
398
+ }
399
+ watchLoopDrag(edge) {
400
+ this.loopDragAbortController?.abort();
401
+ this.loopDragAbortController = new AbortController();
402
+ window.addEventListener("pointermove", (event) => this.updateLoopEdgeFromClientX(edge, event.clientX), { signal: this.loopDragAbortController.signal });
403
+ window.addEventListener("pointerup", () => {
404
+ this.loopDragAbortController?.abort();
405
+ this.loopDragAbortController = undefined;
406
+ }, { once: true, signal: this.loopDragAbortController.signal });
407
+ window.addEventListener("pointercancel", () => {
408
+ this.loopDragAbortController?.abort();
409
+ this.loopDragAbortController = undefined;
410
+ }, { once: true, signal: this.loopDragAbortController.signal });
411
+ }
412
+ updateLoopEdgeFromClientX(edge, clientX) {
413
+ const step = this.loopStepFromClientX(clientX);
414
+ this.updateLoopEdge(edge, step);
415
+ }
416
+ loopStepFromClientX(clientX) {
417
+ const ruler = this.querySelector(".step-sequencer__loop-ruler");
418
+ const rect = ruler?.getBoundingClientRect();
419
+ if (!rect || rect.width <= 0) {
420
+ return this.state.loop.end;
421
+ }
422
+ const progress = (clientX - rect.left) / rect.width;
423
+ return Math.min(stepStepCount, Math.max(1, Math.floor(progress * stepStepCount) + 1));
424
+ }
425
+ updateLoopEdge(edge, step) {
426
+ const previousLoop = this.state.loop;
427
+ const { change, state } = edge === "start"
428
+ ? setStepLoopStart(this.state, step)
429
+ : setStepLoopEnd(this.state, step);
430
+ if (previousLoop.start === state.loop.start &&
431
+ previousLoop.end === state.loop.end) {
432
+ return;
433
+ }
434
+ this.state = state;
435
+ this.render();
436
+ this.dispatchStepChange(change);
437
+ }
438
+ handleNoteToggle(step, lane) {
439
+ const { change, state } = toggleStepNote(this.state, step, lane);
440
+ this.state = state;
441
+ this.render();
442
+ this.dispatchStepChange(change);
443
+ }
444
+ dispatchStepChange(detail) {
445
+ this.dispatchEvent(new CustomEvent("step-change", {
446
+ bubbles: true,
447
+ composed: true,
448
+ detail,
449
+ }));
450
+ }
451
+ }
452
+ function gridDirectionValue(direction) {
453
+ if (direction === "left")
454
+ return "-1";
455
+ if (direction === "off")
456
+ return "0";
457
+ return "1";
458
+ }
459
+ function numberAttribute(element, name) {
460
+ const value = element.getAttribute(name);
461
+ if (value === null || value.trim() === "")
462
+ return undefined;
463
+ const number = Number(value);
464
+ return Number.isFinite(number) ? number : undefined;
465
+ }
466
+ function jsonAttribute(element, name) {
467
+ const value = element.getAttribute(name);
468
+ if (!value)
469
+ return undefined;
470
+ try {
471
+ return JSON.parse(value);
472
+ }
473
+ catch {
474
+ return undefined;
475
+ }
476
+ }
477
+ //# sourceMappingURL=sequencers.js.map