stimeo-ui 0.6.0 → 0.7.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 +62 -0
- data/dist/controllers/alert_dialog_controller.js +75 -4
- data/dist/controllers/avatar_controller.js +47 -5
- data/dist/controllers/character_counter_controller.js +338 -63
- data/dist/controllers/command_palette_controller.js +75 -4
- data/dist/controllers/conditional_fields_controller.js +345 -51
- data/dist/controllers/confirm_controller.js +75 -4
- data/dist/controllers/dialog_controller.js +75 -4
- data/dist/controllers/direct_upload_controller.js +201 -45
- data/dist/controllers/dirty_form_controller.js +192 -29
- data/dist/controllers/dismissible_controller.js +83 -18
- data/dist/controllers/drawer_controller.js +75 -4
- data/dist/controllers/focus_controller.js +75 -4
- data/dist/controllers/form_field_controller.js +280 -62
- data/dist/controllers/form_validation_controller.js +208 -83
- data/dist/controllers/number_input_controller.js +47 -5
- data/dist/controllers/overflow_menu_controller.js +2 -1
- data/dist/controllers/pagination_controller.js +2 -1
- data/dist/controllers/persist_controller.js +432 -122
- data/dist/controllers/popover_controller.js +77 -4
- data/dist/controllers/rating_controller.js +9 -5
- data/dist/controllers/scroll_area_controller.js +462 -162
- data/dist/controllers/separator_controller.js +354 -38
- data/dist/controllers/sidebar_controller.js +83 -10
- data/dist/controllers/submit_once_controller.js +399 -121
- data/dist/controllers/theme_controller.js +8 -6
- data/dist/controllers/tree_view_controller.js +2 -1
- data/dist/index.js +2387 -861
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -92,6 +92,81 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
// src/utils/focus_candidate.ts
|
|
96
|
+
function inheritsFieldsetDisabled(control) {
|
|
97
|
+
let fieldset = control.closest("fieldset[disabled]");
|
|
98
|
+
while (fieldset) {
|
|
99
|
+
const legend = Array.from(fieldset.children).find((child) => child.tagName === "LEGEND");
|
|
100
|
+
if (!legend?.contains(control)) return true;
|
|
101
|
+
fieldset = fieldset.parentElement?.closest("fieldset[disabled]") ?? null;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
function canTakeFocus(element) {
|
|
106
|
+
if (element.closest("[hidden], [inert]")) return false;
|
|
107
|
+
if (element instanceof HTMLInputElement && element.type === "hidden") return false;
|
|
108
|
+
if (!("disabled" in element)) return true;
|
|
109
|
+
if (element.disabled) return false;
|
|
110
|
+
return !inheritsFieldsetDisabled(element);
|
|
111
|
+
}
|
|
112
|
+
var TAB_STOP_CANDIDATE_SELECTOR = [
|
|
113
|
+
"a[href]",
|
|
114
|
+
"area[href]",
|
|
115
|
+
"button",
|
|
116
|
+
"input",
|
|
117
|
+
"select",
|
|
118
|
+
"textarea",
|
|
119
|
+
"summary",
|
|
120
|
+
"iframe",
|
|
121
|
+
"audio[controls]",
|
|
122
|
+
"video[controls]",
|
|
123
|
+
"[tabindex]",
|
|
124
|
+
"[contenteditable]"
|
|
125
|
+
].join(",");
|
|
126
|
+
function isRenderedForFocus(element) {
|
|
127
|
+
const check = element.checkVisibility;
|
|
128
|
+
return typeof check === "function" ? check.call(element, { visibilityProperty: true }) : true;
|
|
129
|
+
}
|
|
130
|
+
function authoredTabindex(element) {
|
|
131
|
+
const value = element.getAttribute("tabindex");
|
|
132
|
+
if (value === null || !/^[+-]?\d+$/.test(value.trim())) return null;
|
|
133
|
+
return Number(value);
|
|
134
|
+
}
|
|
135
|
+
function hasNativeTabStop(element) {
|
|
136
|
+
if (element instanceof HTMLAnchorElement || element instanceof HTMLAreaElement) {
|
|
137
|
+
return element.hasAttribute("href");
|
|
138
|
+
}
|
|
139
|
+
if (element instanceof HTMLButtonElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
if (element instanceof HTMLInputElement) return element.type !== "hidden";
|
|
143
|
+
if (element instanceof HTMLIFrameElement) return true;
|
|
144
|
+
if (element.tagName === "AUDIO" || element.tagName === "VIDEO") {
|
|
145
|
+
return element.hasAttribute("controls");
|
|
146
|
+
}
|
|
147
|
+
if (element instanceof HTMLElement && element.tagName === "SUMMARY") {
|
|
148
|
+
const details = element.parentElement;
|
|
149
|
+
return details instanceof HTMLDetailsElement && Array.from(details.children).find((child) => child.tagName === "SUMMARY") === element;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
function hasEditableTabStop(element) {
|
|
154
|
+
const value = element.getAttribute("contenteditable")?.toLowerCase();
|
|
155
|
+
return value === "" || value === "true" || value === "plaintext-only";
|
|
156
|
+
}
|
|
157
|
+
function isTabStop(element) {
|
|
158
|
+
if (!canTakeFocus(element) || !isRenderedForFocus(element)) return false;
|
|
159
|
+
const tabindex = authoredTabindex(element);
|
|
160
|
+
if (tabindex !== null) return tabindex >= 0;
|
|
161
|
+
return hasNativeTabStop(element) || hasEditableTabStop(element);
|
|
162
|
+
}
|
|
163
|
+
function firstTabStop(root) {
|
|
164
|
+
for (const candidate of root.querySelectorAll(TAB_STOP_CANDIDATE_SELECTOR)) {
|
|
165
|
+
if (isTabStop(candidate)) return candidate;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
95
170
|
// src/utils/scroll_dismiss.ts
|
|
96
171
|
function observeScrollDismiss(element, onScroll) {
|
|
97
172
|
const targets = [...scrollParents(element), window];
|
|
@@ -118,7 +193,7 @@ function isScrollable(element) {
|
|
|
118
193
|
}
|
|
119
194
|
|
|
120
195
|
// src/controllers/popover_controller.ts
|
|
121
|
-
var PopoverController = class
|
|
196
|
+
var PopoverController = class extends Controller {
|
|
122
197
|
static targets = ["trigger", "panel"];
|
|
123
198
|
static values = {
|
|
124
199
|
closeOnScroll: { type: Boolean, default: false }
|
|
@@ -128,8 +203,6 @@ var PopoverController = class _PopoverController extends Controller {
|
|
|
128
203
|
#stopScrollDismiss = null;
|
|
129
204
|
/** Escape-stack membership while open; the shared resolver dismisses via it. */
|
|
130
205
|
#escapeLayer = new EscapeLayer();
|
|
131
|
-
/** Selector for natively focusable elements used to find the first one. */
|
|
132
|
-
static #FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
133
206
|
/** Starts closed and registers the standing dismissal listeners. */
|
|
134
207
|
connect() {
|
|
135
208
|
this.close();
|
|
@@ -181,7 +254,7 @@ var PopoverController = class _PopoverController extends Controller {
|
|
|
181
254
|
}
|
|
182
255
|
/** Moves focus to the first focusable element in the panel, or the panel itself. */
|
|
183
256
|
#focusFirst() {
|
|
184
|
-
const first = this.panelTarget
|
|
257
|
+
const first = firstTabStop(this.panelTarget);
|
|
185
258
|
if (first) {
|
|
186
259
|
first.focus();
|
|
187
260
|
return;
|
|
@@ -32,17 +32,21 @@ var AttributeLease = class {
|
|
|
32
32
|
written: value
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
else element.setAttribute(this.#attribute, value);
|
|
35
|
+
this.#reflect(element, value);
|
|
37
36
|
}
|
|
38
37
|
/** Returns one lease without overwriting a value subsequently authored by a consumer. */
|
|
39
38
|
return(element) {
|
|
40
39
|
const record = this.#records.get(element);
|
|
41
40
|
if (!record) return;
|
|
42
41
|
this.#records.delete(element);
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
42
|
+
const stillOwned = element.getAttribute(this.#attribute) === record.written;
|
|
43
|
+
if (stillOwned) this.#reflect(element, record.original);
|
|
44
|
+
}
|
|
45
|
+
/** Reflects only a real value transition, avoiding self-triggered mutation work. */
|
|
46
|
+
#reflect(element, value) {
|
|
47
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
48
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
49
|
+
else element.setAttribute(this.#attribute, value);
|
|
46
50
|
}
|
|
47
51
|
/** Returns every outstanding lease using the same ownership check as {@link return}. */
|
|
48
52
|
returnAll() {
|