stimeo-ui 0.4.0 → 0.6.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +184 -0
  3. data/dist/controllers/aspect_ratio_controller.js +19 -11
  4. data/dist/controllers/avatar_controller.js +195 -40
  5. data/dist/controllers/breadcrumb_controller.js +5 -1
  6. data/dist/controllers/carousel_controller.js +90 -10
  7. data/dist/controllers/checkbox_controller.js +136 -25
  8. data/dist/controllers/clipboard_controller.js +8 -3
  9. data/dist/controllers/collapsible_controller.js +4 -1
  10. data/dist/controllers/color_picker_controller.js +41 -11
  11. data/dist/controllers/context_menu_controller.js +2 -2
  12. data/dist/controllers/countdown_controller.js +5 -1
  13. data/dist/controllers/date_range_picker_controller.js +162 -31
  14. data/dist/controllers/direct_upload_controller.js +3 -3
  15. data/dist/controllers/empty_state_controller.js +107 -16
  16. data/dist/controllers/file_dropzone_controller.js +26 -3
  17. data/dist/controllers/flash_controller.js +161 -21
  18. data/dist/controllers/form_validation_controller.js +8 -2
  19. data/dist/controllers/frame_loading_controller.js +94 -22
  20. data/dist/controllers/highlight_controller.js +38 -1
  21. data/dist/controllers/idle_controller.js +39 -6
  22. data/dist/controllers/local_time_controller.js +2 -0
  23. data/dist/controllers/masonry_controller.js +1 -1
  24. data/dist/controllers/menubar_controller.js +5 -3
  25. data/dist/controllers/meter_controller.js +3 -1
  26. data/dist/controllers/multi_select_controller.js +460 -151
  27. data/dist/controllers/network_status_controller.js +1 -3
  28. data/dist/controllers/number_input_controller.js +455 -64
  29. data/dist/controllers/overflow_menu_controller.js +5 -1
  30. data/dist/controllers/pagination_controller.js +38 -1
  31. data/dist/controllers/password_strength_controller.js +21 -3
  32. data/dist/controllers/persist_controller.js +24 -5
  33. data/dist/controllers/pointer_drag_controller.js +10 -0
  34. data/dist/controllers/portal_controller.js +10 -0
  35. data/dist/controllers/progress_controller.js +7 -3
  36. data/dist/controllers/radio_group_controller.js +540 -56
  37. data/dist/controllers/range_slider_controller.js +385 -94
  38. data/dist/controllers/rating_controller.js +274 -89
  39. data/dist/controllers/relative_time_controller.js +2 -0
  40. data/dist/controllers/resizable_controller.js +33 -0
  41. data/dist/controllers/roving_controller.js +60 -5
  42. data/dist/controllers/scroll_area_controller.js +155 -23
  43. data/dist/controllers/scroll_visibility_controller.js +33 -0
  44. data/dist/controllers/separator_controller.js +13 -17
  45. data/dist/controllers/skeleton_controller.js +71 -3
  46. data/dist/controllers/slider_controller.js +325 -48
  47. data/dist/controllers/spinner_controller.js +18 -3
  48. data/dist/controllers/step_indicator_controller.js +3 -1
  49. data/dist/controllers/stepper_controller.js +2 -0
  50. data/dist/controllers/switch_controller.js +162 -18
  51. data/dist/controllers/tags_input_controller.js +356 -120
  52. data/dist/controllers/textarea_autosize_controller.js +1 -1
  53. data/dist/controllers/time_picker_controller.js +296 -104
  54. data/dist/controllers/toggle_group_controller.js +378 -55
  55. data/dist/controllers/toolbar_controller.js +5 -3
  56. data/dist/controllers/tree_view_controller.js +24 -4
  57. data/dist/index.js +3811 -1048
  58. data/lib/stimeo/ui/version.rb +1 -1
  59. metadata +2 -2
@@ -1,39 +1,181 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
3
  // src/controllers/switch_controller.ts
4
+
5
+ // src/utils/default_attribute.ts
6
+ function setDefaultAttribute(element, name, value) {
7
+ if (element.hasAttribute(name)) return false;
8
+ element.setAttribute(name, value);
9
+ return true;
10
+ }
11
+
12
+ // src/utils/focus_candidate.ts
13
+ function inheritsFieldsetDisabled(control) {
14
+ let fieldset = control.closest("fieldset[disabled]");
15
+ while (fieldset) {
16
+ const legend = Array.from(fieldset.children).find((child) => child.tagName === "LEGEND");
17
+ if (!legend?.contains(control)) return true;
18
+ fieldset = fieldset.parentElement?.closest("fieldset[disabled]") ?? null;
19
+ }
20
+ return false;
21
+ }
22
+
23
+ // src/utils/interactive_host.ts
24
+ var INTERACTIVE_HOST_SELECTOR = "button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed";
25
+ function isInteractiveHost(element) {
26
+ if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;
27
+ let current = element;
28
+ while (current) {
29
+ const raw = current.getAttribute("contenteditable");
30
+ if (raw !== null) {
31
+ const value = raw.trim().toLowerCase();
32
+ if (value === "false") return false;
33
+ if (value === "" || value === "true" || value === "plaintext-only") return true;
34
+ }
35
+ current = current.parentElement;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ // src/controllers/switch_controller.ts
41
+ var OBSERVED_ATTRIBUTES = [
42
+ "role",
43
+ "aria-checked",
44
+ "tabindex",
45
+ "type",
46
+ "href",
47
+ "contenteditable",
48
+ "controls"
49
+ ];
50
+ var OBSERVED_ANCESTOR_ATTRIBUTES = ["contenteditable"];
4
51
  var SwitchController = class extends Controller {
5
52
  static actions = ["onKeydown", "toggle"];
6
53
  static events = ["changed"];
54
+ /** Defaults this instance introduced and may therefore remove safely. */
55
+ #ownedDefaults = /* @__PURE__ */ new Set();
56
+ /** Controller writes that must not be mistaken for authored morph changes. */
57
+ #internalAttributeValues = /* @__PURE__ */ new Map();
58
+ #attributeObserver = new MutationObserver((records) => {
59
+ this.#releaseAuthoredDefaults(records);
60
+ this.#attributeObserver.disconnect();
61
+ this.#reconcileDefaults();
62
+ this.#observeAttributes();
63
+ });
64
+ /** Blocks disabled pointer/native-key activation before consumer click handlers. */
65
+ #onClickCapture = (event) => {
66
+ if (!this.#isSupportedHost || !this.#isActivationDisabled) return;
67
+ event.preventDefault();
68
+ event.stopImmediatePropagation();
69
+ };
7
70
  /** Ensures the switch exposes a role and is keyboard-reachable. */
8
71
  connect() {
9
- if (!this.element.hasAttribute("role")) {
10
- this.element.setAttribute("role", "switch");
11
- }
12
- if (!this.element.hasAttribute("aria-checked")) {
13
- this.element.setAttribute("aria-checked", "false");
14
- }
15
- if (!(this.element instanceof HTMLButtonElement) && !this.element.hasAttribute("tabindex")) {
16
- this.element.setAttribute("tabindex", "0");
17
- }
72
+ this.#reconcileDefaults();
73
+ this.element.addEventListener("click", this.#onClickCapture, true);
74
+ this.#observeAttributes();
75
+ }
76
+ /** Releases the explicit click guard and retained-element attribute observer. */
77
+ disconnect() {
78
+ this.element.removeEventListener("click", this.#onClickCapture, true);
79
+ this.#attributeObserver.disconnect();
80
+ this.#internalAttributeValues.clear();
18
81
  }
19
82
  /** Toggles the checked state. Bound via `data-action` (click). */
20
83
  toggle() {
84
+ if (!this.#isSupportedHost || this.#isActivationDisabled) return;
21
85
  this.#checked = !this.#checked;
22
86
  }
23
87
  /**
24
- * Activates the switch on Space/Enter for non-native hosts and prevents the
25
- * default Space scroll. Bound via `data-action` (keydown). Native `<button>`
26
- * hosts are skipped because the browser already turns Space/Enter into a click,
27
- * which would otherwise toggle the switch twice.
88
+ * Activates the switch on Space/Enter for non-native hosts and suppresses key
89
+ * repeat. Bound via `data-action` (keydown). A native `<button type="button">`
90
+ * owns the initial key-to-click synthesis; repeated keydowns are canceled before
91
+ * the browser can synthesize additional clicks.
28
92
  */
29
93
  onKeydown(event) {
30
94
  if (event.defaultPrevented) return;
31
- if (this.element instanceof HTMLButtonElement) return;
32
- if (event.repeat) return;
33
- if (event.key === " " || event.key === "Enter") {
95
+ if (!this.#isSupportedHost) return;
96
+ if (event.key !== " " && event.key !== "Enter") return;
97
+ if (this.#isActivationDisabled) {
34
98
  event.preventDefault();
35
- this.toggle();
99
+ event.stopImmediatePropagation();
100
+ return;
101
+ }
102
+ if (this.element instanceof HTMLButtonElement) {
103
+ if (event.repeat) event.preventDefault();
104
+ return;
105
+ }
106
+ event.preventDefault();
107
+ if (event.repeat) return;
108
+ this.toggle();
109
+ }
110
+ /** Re-adds only missing defaults, preserving every authored attribute value. */
111
+ #reconcileDefaults() {
112
+ if (!this.#isSupportedHost) {
113
+ this.#removeOwnedDefaults();
114
+ return;
115
+ }
116
+ this.#setOwnedDefault("role", "switch");
117
+ this.#setOwnedDefault("aria-checked", "false");
118
+ if (!(this.element instanceof HTMLButtonElement)) {
119
+ this.#setOwnedDefault("tabindex", "0");
120
+ }
121
+ }
122
+ /** Records a newly introduced default without claiming authored markup. */
123
+ #setOwnedDefault(name, value) {
124
+ if (setDefaultAttribute(this.element, name, value)) this.#ownedDefaults.add(name);
125
+ }
126
+ /**
127
+ * Gives ownership back when a retained-element morph authors a value. Missing
128
+ * owned attributes stay owned so reconciliation can restore them.
129
+ */
130
+ #releaseAuthoredDefaults(records) {
131
+ for (const record of records) {
132
+ if (record.target === this.element) {
133
+ const name = record.attributeName;
134
+ if (name && this.#ownedDefaults.has(name)) {
135
+ const value = this.element.getAttribute(name);
136
+ if (value !== null && this.#internalAttributeValues.get(name) !== value) {
137
+ this.#ownedDefaults.delete(name);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ this.#internalAttributeValues.clear();
143
+ }
144
+ /** Removes only defaults introduced by this instance when a host becomes invalid. */
145
+ #removeOwnedDefaults() {
146
+ for (const name of this.#ownedDefaults) this.element.removeAttribute(name);
147
+ this.#ownedDefaults.clear();
148
+ this.#internalAttributeValues.clear();
149
+ }
150
+ /** Watches retained host attributes that Turbo can morph without reconnecting. */
151
+ #observeAttributes() {
152
+ this.#attributeObserver.observe(this.element, {
153
+ attributes: true,
154
+ attributeFilter: OBSERVED_ATTRIBUTES
155
+ });
156
+ let ancestor = this.element.parentElement;
157
+ while (ancestor) {
158
+ this.#attributeObserver.observe(ancestor, {
159
+ attributes: true,
160
+ attributeFilter: OBSERVED_ANCESTOR_ATTRIBUTES
161
+ });
162
+ ancestor = ancestor.parentElement;
163
+ }
164
+ }
165
+ /** Whether the host has one activation model that this controller can own. */
166
+ get #isSupportedHost() {
167
+ if (this.element instanceof HTMLButtonElement) return this.element.type === "button";
168
+ return !isInteractiveHost(this.element);
169
+ }
170
+ /** Whether ARIA or native HTML semantics make the supported switch inoperable. */
171
+ get #isActivationDisabled() {
172
+ let current = this.element;
173
+ while (current) {
174
+ if (current.getAttribute("aria-disabled") === "true") return true;
175
+ current = current.parentElement;
36
176
  }
177
+ if (!(this.element instanceof HTMLButtonElement)) return false;
178
+ return this.element.disabled || inheritsFieldsetDisabled(this.element);
37
179
  }
38
180
  /** Whether the switch is currently on. */
39
181
  get #checked() {
@@ -41,7 +183,9 @@ var SwitchController = class extends Controller {
41
183
  }
42
184
  /** Reflects the new state on `aria-checked` and notifies listeners. */
43
185
  set #checked(value) {
44
- this.element.setAttribute("aria-checked", value ? "true" : "false");
186
+ const reflected = value ? "true" : "false";
187
+ this.#internalAttributeValues.set("aria-checked", reflected);
188
+ this.element.setAttribute("aria-checked", reflected);
45
189
  this.dispatch("changed", { detail: { checked: value } });
46
190
  }
47
191
  };