@unabridged/midwest 0.19.2 → 0.20.1

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.
Files changed (24) hide show
  1. package/README.md +1 -1
  2. package/app/assets/javascript/midwest/index.ts +6 -0
  3. package/app/assets/javascript/midwest/template_helpers.ts +27 -0
  4. package/app/assets/javascript/midwest.js +656 -0
  5. package/app/assets/javascript/midwest.js.map +1 -1
  6. package/app/assets/stylesheets/midwest.css +1 -1
  7. package/app/assets/stylesheets/midwest.tailwind.css +5 -1
  8. package/dist/css/midwest.css +1 -1
  9. package/dist/javascript/collection/app/assets/javascript/midwest/index.js +6 -0
  10. package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
  11. package/dist/javascript/collection/app/assets/javascript/midwest/template_helpers.js +10 -0
  12. package/dist/javascript/collection/app/assets/javascript/midwest/template_helpers.js.map +1 -0
  13. package/dist/javascript/collection/app/components/midwest/card_component/card_component_controller.js.map +1 -1
  14. package/dist/javascript/collection/app/components/midwest/command_palette_component/command_palette_component_controller.js +350 -0
  15. package/dist/javascript/collection/app/components/midwest/command_palette_component/command_palette_component_controller.js.map +1 -0
  16. package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js +24 -0
  17. package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js.map +1 -1
  18. package/dist/javascript/collection/app/components/midwest/show_if_component/show_if_component_controller.js +80 -0
  19. package/dist/javascript/collection/app/components/midwest/show_if_component/show_if_component_controller.js.map +1 -0
  20. package/dist/javascript/collection/app/components/midwest/tree_view_component/tree_view_component_controller.js +204 -0
  21. package/dist/javascript/collection/app/components/midwest/tree_view_component/tree_view_component_controller.js.map +1 -0
  22. package/dist/javascript/midwest.js +656 -0
  23. package/dist/javascript/midwest.js.map +1 -1
  24. package/package.json +1 -1
@@ -0,0 +1,80 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+
3
+ class ShowIf extends Controller {
4
+ static values = {
5
+ watch: { type: String, default: "" },
6
+ is: { type: String, default: "" },
7
+ negate: { type: Boolean, default: false }
8
+ };
9
+ sourceElement = null;
10
+ listenedElements = [];
11
+ changeListener = null;
12
+ connect() {
13
+ if (!this.watchValue)
14
+ return;
15
+ this.sourceElement = document.querySelector(this.watchValue);
16
+ if (!this.sourceElement)
17
+ return;
18
+ this.changeListener = () => this.evaluate();
19
+ if (this.sourceElement instanceof HTMLInputElement && this.sourceElement.type === "radio") {
20
+ const radios = document.querySelectorAll(
21
+ `input[type="radio"][name="${this.sourceElement.name}"]`
22
+ );
23
+ const listener = this.changeListener;
24
+ radios.forEach((radio) => {
25
+ radio.addEventListener("change", listener);
26
+ this.listenedElements.push(radio);
27
+ });
28
+ } else {
29
+ this.sourceElement.addEventListener("input", this.changeListener);
30
+ this.sourceElement.addEventListener("change", this.changeListener);
31
+ this.listenedElements.push(this.sourceElement);
32
+ }
33
+ this.evaluate();
34
+ }
35
+ disconnect() {
36
+ if (this.changeListener) {
37
+ for (const el of this.listenedElements) {
38
+ el.removeEventListener("input", this.changeListener);
39
+ el.removeEventListener("change", this.changeListener);
40
+ }
41
+ }
42
+ this.changeListener = null;
43
+ this.sourceElement = null;
44
+ this.listenedElements = [];
45
+ }
46
+ evaluate() {
47
+ const el = this.sourceElement;
48
+ if (!el)
49
+ return;
50
+ const currentValue = this.readValue(el);
51
+ const match = currentValue === this.isValue;
52
+ const visible = this.negateValue ? !match : match;
53
+ this.element.toggleAttribute("hidden", !visible);
54
+ this.element.toggleAttribute("disabled", !visible);
55
+ }
56
+ readValue(el) {
57
+ if (el instanceof HTMLInputElement) {
58
+ if (el.type === "radio") {
59
+ const checked = document.querySelector(
60
+ `input[name="${el.name}"]:checked`
61
+ );
62
+ return checked ? checked.value : "";
63
+ }
64
+ if (el.type === "checkbox") {
65
+ return el.checked ? el.value || "true" : "";
66
+ }
67
+ return el.value;
68
+ }
69
+ if (el instanceof HTMLSelectElement) {
70
+ return el.value;
71
+ }
72
+ if (el instanceof HTMLTextAreaElement) {
73
+ return el.value;
74
+ }
75
+ return el.getAttribute("value") ?? "";
76
+ }
77
+ }
78
+
79
+ export { ShowIf as default };
80
+ //# sourceMappingURL=show_if_component_controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"show_if_component_controller.js","sources":["../../../../../../../app/components/midwest/show_if_component/show_if_component_controller.ts"],"sourcesContent":["\nimport { Controller } from '@hotwired/stimulus'\n\nexport default class ShowIf extends Controller<HTMLElement> {\n static values = {\n watch: { type: String, default: '' },\n is: { type: String, default: '' },\n negate: { type: Boolean, default: false }\n }\n\n declare watchValue: string\n declare isValue: string\n declare negateValue: boolean\n\n private sourceElement: Element | null = null\n private listenedElements: Element[] = []\n private changeListener: (() => void) | null = null\n\n connect () {\n if (!this.watchValue) return\n\n this.sourceElement = document.querySelector(this.watchValue)\n if (!this.sourceElement) return\n\n this.changeListener = () => this.evaluate()\n\n // For radio buttons, listen on all radios in the group so that\n // selecting a *different* radio still triggers re-evaluation.\n if (this.sourceElement instanceof HTMLInputElement && this.sourceElement.type === 'radio') {\n const radios = document.querySelectorAll<HTMLInputElement>(\n `input[type=\"radio\"][name=\"${this.sourceElement.name}\"]`\n )\n const listener = this.changeListener\n radios.forEach((radio) => {\n radio.addEventListener('change', listener)\n this.listenedElements.push(radio)\n })\n } else {\n this.sourceElement.addEventListener('input', this.changeListener)\n this.sourceElement.addEventListener('change', this.changeListener)\n this.listenedElements.push(this.sourceElement)\n }\n\n this.evaluate()\n }\n\n disconnect () {\n if (this.changeListener) {\n for (const el of this.listenedElements) {\n el.removeEventListener('input', this.changeListener)\n el.removeEventListener('change', this.changeListener)\n }\n }\n this.changeListener = null\n this.sourceElement = null\n this.listenedElements = []\n }\n\n private evaluate () {\n const el = this.sourceElement\n if (!el) return\n\n const currentValue = this.readValue(el)\n const match = currentValue === this.isValue\n const visible = this.negateValue ? !match : match\n\n this.element.toggleAttribute('hidden', !visible)\n this.element.toggleAttribute('disabled', !visible)\n }\n\n private readValue (el: Element): string {\n if (el instanceof HTMLInputElement) {\n if (el.type === 'radio') {\n const checked = document.querySelector<HTMLInputElement>(\n `input[name=\"${el.name}\"]:checked`\n )\n return checked ? checked.value : ''\n }\n if (el.type === 'checkbox') {\n return el.checked ? (el.value || 'true') : ''\n }\n return el.value\n }\n\n if (el instanceof HTMLSelectElement) {\n return el.value\n }\n\n if (el instanceof HTMLTextAreaElement) {\n return el.value\n }\n\n return el.getAttribute('value') ?? ''\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,204 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+
3
+ class TreeView extends Controller {
4
+ // ── Expand / Collapse ───────────────────────────────────────────────
5
+ toggle(event) {
6
+ const chevron = event.currentTarget.closest(".tree-node-chevron");
7
+ if (!chevron)
8
+ return;
9
+ const node = chevron.closest(".midwest-tree-node");
10
+ if (!node)
11
+ return;
12
+ const expanded = node.getAttribute("aria-expanded") === "true";
13
+ node.setAttribute("aria-expanded", String(!expanded));
14
+ this.focusNode(node);
15
+ }
16
+ // ── Checkbox Cascading ──────────────────────────────────────────────
17
+ checkboxChanged(event) {
18
+ const checkbox = event.target;
19
+ const node = checkbox.closest(".midwest-tree-node");
20
+ if (!node)
21
+ return;
22
+ const descendants = node.querySelectorAll(
23
+ ".tree-node-children .tree-node-checkbox"
24
+ );
25
+ for (const desc of descendants) {
26
+ desc.checked = checkbox.checked;
27
+ desc.indeterminate = false;
28
+ }
29
+ this.updateAncestors(node);
30
+ }
31
+ updateAncestors(node) {
32
+ const parentGroup = node.parentElement;
33
+ if (!parentGroup || !parentGroup.classList.contains("tree-node-children"))
34
+ return;
35
+ const parentNode = parentGroup.closest(".midwest-tree-node");
36
+ if (!parentNode)
37
+ return;
38
+ const parentCheckbox = parentNode.querySelector(
39
+ ":scope > .tree-node-trigger > .tree-node-checkbox"
40
+ );
41
+ if (!parentCheckbox)
42
+ return;
43
+ const childCheckboxes = parentGroup.querySelectorAll(
44
+ ":scope > .midwest-tree-node > .tree-node-trigger > .tree-node-checkbox"
45
+ );
46
+ const total = childCheckboxes.length;
47
+ let checked = 0;
48
+ let indeterminate = 0;
49
+ for (const cb of childCheckboxes) {
50
+ if (cb.indeterminate)
51
+ indeterminate++;
52
+ else if (cb.checked)
53
+ checked++;
54
+ }
55
+ if (indeterminate > 0 || checked > 0 && checked < total) {
56
+ parentCheckbox.checked = false;
57
+ parentCheckbox.indeterminate = true;
58
+ } else if (checked === total) {
59
+ parentCheckbox.checked = true;
60
+ parentCheckbox.indeterminate = false;
61
+ } else {
62
+ parentCheckbox.checked = false;
63
+ parentCheckbox.indeterminate = false;
64
+ }
65
+ this.updateAncestors(parentNode);
66
+ }
67
+ // ── Keyboard Navigation (WAI-ARIA TreeView) ────────────────────────
68
+ connect() {
69
+ this.element.addEventListener("keydown", this.handleKeydown);
70
+ const firstNode = this.element.querySelector(".midwest-tree-node");
71
+ if (firstNode) {
72
+ firstNode.setAttribute("tabindex", "0");
73
+ }
74
+ }
75
+ disconnect() {
76
+ this.element.removeEventListener("keydown", this.handleKeydown);
77
+ }
78
+ handleKeydown = (event) => {
79
+ const target = event.target.closest(".midwest-tree-node");
80
+ if (!target)
81
+ return;
82
+ switch (event.key) {
83
+ case "ArrowDown":
84
+ event.preventDefault();
85
+ this.moveFocus(target, "next");
86
+ break;
87
+ case "ArrowUp":
88
+ event.preventDefault();
89
+ this.moveFocus(target, "previous");
90
+ break;
91
+ case "ArrowRight":
92
+ event.preventDefault();
93
+ this.handleArrowRight(target);
94
+ break;
95
+ case "ArrowLeft":
96
+ event.preventDefault();
97
+ this.handleArrowLeft(target);
98
+ break;
99
+ case "Home":
100
+ event.preventDefault();
101
+ this.focusFirst();
102
+ break;
103
+ case "End":
104
+ event.preventDefault();
105
+ this.focusLast();
106
+ break;
107
+ case "Enter":
108
+ case " ":
109
+ event.preventDefault();
110
+ this.handleActivate(target);
111
+ break;
112
+ }
113
+ };
114
+ getVisibleNodes() {
115
+ const nodes = [];
116
+ const walk = (container) => {
117
+ const children = container.querySelectorAll(
118
+ ":scope > .midwest-tree-node"
119
+ );
120
+ for (const child of children) {
121
+ nodes.push(child);
122
+ if (child.getAttribute("aria-expanded") === "true") {
123
+ const group = child.querySelector(":scope > .tree-node-children");
124
+ if (group)
125
+ walk(group);
126
+ }
127
+ }
128
+ };
129
+ walk(this.element);
130
+ return nodes;
131
+ }
132
+ moveFocus(current, direction) {
133
+ const visible = this.getVisibleNodes();
134
+ const index = visible.indexOf(current);
135
+ if (index === -1)
136
+ return;
137
+ const nextIndex = direction === "next" ? index + 1 : index - 1;
138
+ if (nextIndex >= 0 && nextIndex < visible.length) {
139
+ this.focusNode(visible[nextIndex]);
140
+ }
141
+ }
142
+ handleArrowRight(node) {
143
+ const isExpanded = node.getAttribute("aria-expanded");
144
+ if (isExpanded === "false") {
145
+ node.setAttribute("aria-expanded", "true");
146
+ } else if (isExpanded === "true") {
147
+ const firstChild = node.querySelector(
148
+ ":scope > .tree-node-children > .midwest-tree-node"
149
+ );
150
+ if (firstChild)
151
+ this.focusNode(firstChild);
152
+ }
153
+ }
154
+ handleArrowLeft(node) {
155
+ const isExpanded = node.getAttribute("aria-expanded");
156
+ if (isExpanded === "true") {
157
+ node.setAttribute("aria-expanded", "false");
158
+ } else {
159
+ const parentGroup = node.parentElement;
160
+ if (parentGroup && parentGroup.classList.contains("tree-node-children")) {
161
+ const parentNode = parentGroup.closest(".midwest-tree-node");
162
+ if (parentNode)
163
+ this.focusNode(parentNode);
164
+ }
165
+ }
166
+ }
167
+ handleActivate(node) {
168
+ const checkbox = node.querySelector(
169
+ ":scope > .tree-node-trigger > .tree-node-checkbox"
170
+ );
171
+ if (checkbox) {
172
+ checkbox.checked = !checkbox.checked;
173
+ checkbox.dispatchEvent(new Event("change", { bubbles: true }));
174
+ return;
175
+ }
176
+ const expanded = node.getAttribute("aria-expanded");
177
+ if (expanded !== null) {
178
+ node.setAttribute("aria-expanded", String(expanded !== "true"));
179
+ }
180
+ }
181
+ focusNode(node) {
182
+ const current = this.element.querySelector(
183
+ '.midwest-tree-node[tabindex="0"]'
184
+ );
185
+ if (current)
186
+ current.setAttribute("tabindex", "-1");
187
+ node.setAttribute("tabindex", "0");
188
+ node.focus();
189
+ }
190
+ focusFirst() {
191
+ const first = this.element.querySelector(".midwest-tree-node");
192
+ if (first)
193
+ this.focusNode(first);
194
+ }
195
+ focusLast() {
196
+ const visible = this.getVisibleNodes();
197
+ if (visible.length > 0) {
198
+ this.focusNode(visible[visible.length - 1]);
199
+ }
200
+ }
201
+ }
202
+
203
+ export { TreeView as default };
204
+ //# sourceMappingURL=tree_view_component_controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree_view_component_controller.js","sources":["../../../../../../../app/components/midwest/tree_view_component/tree_view_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\nexport default class TreeView extends Controller<HTMLElement> {\n // ── Expand / Collapse ───────────────────────────────────────────────\n\n toggle (event: Event) {\n const chevron = (event.currentTarget as HTMLElement).closest('.tree-node-chevron')\n if (!chevron) return\n\n const node = chevron.closest('.midwest-tree-node') as HTMLElement | null\n if (!node) return\n\n const expanded = node.getAttribute('aria-expanded') === 'true'\n node.setAttribute('aria-expanded', String(!expanded))\n\n this.focusNode(node)\n }\n\n // ── Checkbox Cascading ──────────────────────────────────────────────\n\n checkboxChanged (event: Event) {\n const checkbox = event.target as HTMLInputElement\n const node = checkbox.closest('.midwest-tree-node') as HTMLElement | null\n if (!node) return\n\n // Cascade down: set all descendant checkboxes to match\n const descendants = node.querySelectorAll<HTMLInputElement>(\n '.tree-node-children .tree-node-checkbox'\n )\n for (const desc of descendants) {\n desc.checked = checkbox.checked\n desc.indeterminate = false\n }\n\n // Cascade up: update ancestor checkboxes\n this.updateAncestors(node)\n }\n\n private updateAncestors (node: HTMLElement) {\n const parentGroup = node.parentElement\n if (!parentGroup || !parentGroup.classList.contains('tree-node-children')) return\n\n const parentNode = parentGroup.closest('.midwest-tree-node') as HTMLElement | null\n if (!parentNode) return\n\n const parentCheckbox = parentNode.querySelector<HTMLInputElement>(\n ':scope > .tree-node-trigger > .tree-node-checkbox'\n )\n if (!parentCheckbox) return\n\n const childCheckboxes = parentGroup.querySelectorAll<HTMLInputElement>(\n ':scope > .midwest-tree-node > .tree-node-trigger > .tree-node-checkbox'\n )\n\n const total = childCheckboxes.length\n let checked = 0\n let indeterminate = 0\n\n for (const cb of childCheckboxes) {\n if (cb.indeterminate) indeterminate++\n else if (cb.checked) checked++\n }\n\n if (indeterminate > 0 || (checked > 0 && checked < total)) {\n parentCheckbox.checked = false\n parentCheckbox.indeterminate = true\n } else if (checked === total) {\n parentCheckbox.checked = true\n parentCheckbox.indeterminate = false\n } else {\n parentCheckbox.checked = false\n parentCheckbox.indeterminate = false\n }\n\n this.updateAncestors(parentNode)\n }\n\n // ── Keyboard Navigation (WAI-ARIA TreeView) ────────────────────────\n\n connect () {\n this.element.addEventListener('keydown', this.handleKeydown)\n\n // Set the first node to tabindex 0 for initial focus\n const firstNode = this.element.querySelector<HTMLElement>('.midwest-tree-node')\n if (firstNode) {\n firstNode.setAttribute('tabindex', '0')\n }\n }\n\n disconnect () {\n this.element.removeEventListener('keydown', this.handleKeydown)\n }\n\n private handleKeydown = (event: KeyboardEvent) => {\n const target = (event.target as HTMLElement).closest('.midwest-tree-node') as HTMLElement | null\n if (!target) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n this.moveFocus(target, 'next')\n break\n case 'ArrowUp':\n event.preventDefault()\n this.moveFocus(target, 'previous')\n break\n case 'ArrowRight':\n event.preventDefault()\n this.handleArrowRight(target)\n break\n case 'ArrowLeft':\n event.preventDefault()\n this.handleArrowLeft(target)\n break\n case 'Home':\n event.preventDefault()\n this.focusFirst()\n break\n case 'End':\n event.preventDefault()\n this.focusLast()\n break\n case 'Enter':\n case ' ':\n event.preventDefault()\n this.handleActivate(target)\n break\n }\n }\n\n private getVisibleNodes (): HTMLElement[] {\n const nodes: HTMLElement[] = []\n\n const walk = (container: Element) => {\n const children = container.querySelectorAll<HTMLElement>(\n ':scope > .midwest-tree-node'\n )\n for (const child of children) {\n nodes.push(child)\n if (child.getAttribute('aria-expanded') === 'true') {\n const group = child.querySelector(':scope > .tree-node-children')\n if (group) walk(group)\n }\n }\n }\n\n walk(this.element)\n return nodes\n }\n\n private moveFocus (current: HTMLElement, direction: 'next' | 'previous') {\n const visible = this.getVisibleNodes()\n const index = visible.indexOf(current)\n if (index === -1) return\n\n const nextIndex = direction === 'next' ? index + 1 : index - 1\n if (nextIndex >= 0 && nextIndex < visible.length) {\n this.focusNode(visible[nextIndex])\n }\n }\n\n private handleArrowRight (node: HTMLElement) {\n const isExpanded = node.getAttribute('aria-expanded')\n\n if (isExpanded === 'false') {\n // Expand the node\n node.setAttribute('aria-expanded', 'true')\n } else if (isExpanded === 'true') {\n // Move focus to first child\n const firstChild = node.querySelector<HTMLElement>(\n ':scope > .tree-node-children > .midwest-tree-node'\n )\n if (firstChild) this.focusNode(firstChild)\n }\n // Leaf nodes: do nothing\n }\n\n private handleArrowLeft (node: HTMLElement) {\n const isExpanded = node.getAttribute('aria-expanded')\n\n if (isExpanded === 'true') {\n // Collapse the node\n node.setAttribute('aria-expanded', 'false')\n } else {\n // Move focus to parent node\n const parentGroup = node.parentElement\n if (parentGroup && parentGroup.classList.contains('tree-node-children')) {\n const parentNode = parentGroup.closest('.midwest-tree-node') as HTMLElement | null\n if (parentNode) this.focusNode(parentNode)\n }\n }\n }\n\n private handleActivate (node: HTMLElement) {\n const checkbox = node.querySelector<HTMLInputElement>(\n ':scope > .tree-node-trigger > .tree-node-checkbox'\n )\n if (checkbox) {\n checkbox.checked = !checkbox.checked\n checkbox.dispatchEvent(new Event('change', { bubbles: true }))\n return\n }\n\n // Toggle expand/collapse for branch nodes\n const expanded = node.getAttribute('aria-expanded')\n if (expanded !== null) {\n node.setAttribute('aria-expanded', String(expanded !== 'true'))\n }\n }\n\n private focusNode (node: HTMLElement) {\n // Remove tabindex from currently focused node\n const current = this.element.querySelector<HTMLElement>(\n '.midwest-tree-node[tabindex=\"0\"]'\n )\n if (current) current.setAttribute('tabindex', '-1')\n\n node.setAttribute('tabindex', '0')\n node.focus()\n }\n\n private focusFirst () {\n const first = this.element.querySelector<HTMLElement>('.midwest-tree-node')\n if (first) this.focusNode(first)\n }\n\n private focusLast () {\n const visible = this.getVisibleNodes()\n if (visible.length > 0) {\n this.focusNode(visible[visible.length - 1])\n }\n }\n}\n"],"names":[],"mappings":"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"}