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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +184 -0
- data/dist/controllers/aspect_ratio_controller.js +19 -11
- data/dist/controllers/avatar_controller.js +195 -40
- data/dist/controllers/breadcrumb_controller.js +5 -1
- data/dist/controllers/carousel_controller.js +90 -10
- data/dist/controllers/checkbox_controller.js +136 -25
- data/dist/controllers/clipboard_controller.js +8 -3
- data/dist/controllers/collapsible_controller.js +4 -1
- data/dist/controllers/color_picker_controller.js +41 -11
- data/dist/controllers/context_menu_controller.js +2 -2
- data/dist/controllers/countdown_controller.js +5 -1
- data/dist/controllers/date_range_picker_controller.js +162 -31
- data/dist/controllers/direct_upload_controller.js +3 -3
- data/dist/controllers/empty_state_controller.js +107 -16
- data/dist/controllers/file_dropzone_controller.js +26 -3
- data/dist/controllers/flash_controller.js +161 -21
- data/dist/controllers/form_validation_controller.js +8 -2
- data/dist/controllers/frame_loading_controller.js +94 -22
- data/dist/controllers/highlight_controller.js +38 -1
- data/dist/controllers/idle_controller.js +39 -6
- data/dist/controllers/local_time_controller.js +2 -0
- data/dist/controllers/masonry_controller.js +1 -1
- data/dist/controllers/menubar_controller.js +5 -3
- data/dist/controllers/meter_controller.js +3 -1
- data/dist/controllers/multi_select_controller.js +460 -151
- data/dist/controllers/network_status_controller.js +1 -3
- data/dist/controllers/number_input_controller.js +455 -64
- data/dist/controllers/overflow_menu_controller.js +5 -1
- data/dist/controllers/pagination_controller.js +38 -1
- data/dist/controllers/password_strength_controller.js +21 -3
- data/dist/controllers/persist_controller.js +24 -5
- data/dist/controllers/pointer_drag_controller.js +10 -0
- data/dist/controllers/portal_controller.js +10 -0
- data/dist/controllers/progress_controller.js +7 -3
- data/dist/controllers/radio_group_controller.js +540 -56
- data/dist/controllers/range_slider_controller.js +385 -94
- data/dist/controllers/rating_controller.js +274 -89
- data/dist/controllers/relative_time_controller.js +2 -0
- data/dist/controllers/resizable_controller.js +33 -0
- data/dist/controllers/roving_controller.js +60 -5
- data/dist/controllers/scroll_area_controller.js +155 -23
- data/dist/controllers/scroll_visibility_controller.js +33 -0
- data/dist/controllers/separator_controller.js +13 -17
- data/dist/controllers/skeleton_controller.js +71 -3
- data/dist/controllers/slider_controller.js +325 -48
- data/dist/controllers/spinner_controller.js +18 -3
- data/dist/controllers/step_indicator_controller.js +3 -1
- data/dist/controllers/stepper_controller.js +2 -0
- data/dist/controllers/switch_controller.js +162 -18
- data/dist/controllers/tags_input_controller.js +356 -120
- data/dist/controllers/textarea_autosize_controller.js +1 -1
- data/dist/controllers/time_picker_controller.js +296 -104
- data/dist/controllers/toggle_group_controller.js +378 -55
- data/dist/controllers/toolbar_controller.js +5 -3
- data/dist/controllers/tree_view_controller.js +24 -4
- data/dist/index.js +3811 -1048
- data/lib/stimeo/ui/version.rb +1 -1
- 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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
|
|
32
|
-
if (event.
|
|
33
|
-
if (
|
|
95
|
+
if (!this.#isSupportedHost) return;
|
|
96
|
+
if (event.key !== " " && event.key !== "Enter") return;
|
|
97
|
+
if (this.#isActivationDisabled) {
|
|
34
98
|
event.preventDefault();
|
|
35
|
-
|
|
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
|
-
|
|
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
|
};
|