stimeo-ui 0.6.0 → 0.8.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 +124 -0
- data/dist/controllers/alert_dialog_controller.js +75 -4
- data/dist/controllers/auto_submit_controller.js +94 -16
- data/dist/controllers/avatar_controller.js +47 -5
- data/dist/controllers/carousel_controller.js +451 -100
- 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/currency_input_controller.js +305 -74
- data/dist/controllers/dialog_controller.js +75 -4
- data/dist/controllers/direct_upload_controller.js +212 -46
- 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/file_dropzone_controller.js +386 -63
- data/dist/controllers/flash_controller.js +3 -1
- 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/frame_loading_controller.js +2 -1
- data/dist/controllers/input_mask_controller.js +251 -76
- data/dist/controllers/nested_form_controller.js +450 -42
- data/dist/controllers/number_input_controller.js +47 -5
- data/dist/controllers/otp_controller.js +472 -114
- 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/spinner_controller.js +7 -5
- data/dist/controllers/submit_once_controller.js +402 -122
- data/dist/controllers/textarea_autosize_controller.js +131 -3
- data/dist/controllers/theme_controller.js +8 -6
- data/dist/controllers/tree_view_controller.js +2 -1
- data/dist/index.js +4293 -1447
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
// src/controllers/textarea_autosize_controller.ts
|
|
4
|
+
|
|
5
|
+
// src/utils/layout_observer.ts
|
|
6
|
+
var LayoutObserver = class {
|
|
7
|
+
#callback;
|
|
8
|
+
#resizeObserverFactory;
|
|
9
|
+
#resizeObserver = null;
|
|
10
|
+
#observingViewport = false;
|
|
11
|
+
/** Stable bound handler so add/removeEventListener target the same reference. */
|
|
12
|
+
#handleViewportResize = () => {
|
|
13
|
+
this.#callback();
|
|
14
|
+
};
|
|
15
|
+
constructor(callback, options = {}) {
|
|
16
|
+
this.#callback = callback;
|
|
17
|
+
this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Starts observing an element's size. Repeated calls observe additional
|
|
21
|
+
* elements through the same shared observer. No-ops when no
|
|
22
|
+
* `ResizeObserver` implementation is available.
|
|
23
|
+
*/
|
|
24
|
+
observe(element) {
|
|
25
|
+
if (!this.#resizeObserverFactory) return;
|
|
26
|
+
if (!this.#resizeObserver) {
|
|
27
|
+
this.#resizeObserver = this.#resizeObserverFactory(() => {
|
|
28
|
+
this.#callback();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
this.#resizeObserver.observe(element);
|
|
32
|
+
}
|
|
33
|
+
/** Stops observing a single element while leaving any others in place. */
|
|
34
|
+
unobserve(element) {
|
|
35
|
+
this.#resizeObserver?.unobserve(element);
|
|
36
|
+
}
|
|
37
|
+
/** Starts observing viewport resizes. Idempotent: the listener is added once. */
|
|
38
|
+
observeViewport() {
|
|
39
|
+
if (this.#observingViewport) return;
|
|
40
|
+
this.#observingViewport = true;
|
|
41
|
+
window.addEventListener("resize", this.#handleViewportResize);
|
|
42
|
+
}
|
|
43
|
+
/** Stops observing viewport resizes without affecting element observation. */
|
|
44
|
+
unobserveViewport() {
|
|
45
|
+
if (!this.#observingViewport) return;
|
|
46
|
+
this.#observingViewport = false;
|
|
47
|
+
window.removeEventListener("resize", this.#handleViewportResize);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Releases every observation: disconnects the {@link ResizeObserver} and
|
|
51
|
+
* removes the viewport listener. Safe to call multiple times. Call this from a
|
|
52
|
+
* controller's `disconnect()`.
|
|
53
|
+
*/
|
|
54
|
+
disconnect() {
|
|
55
|
+
this.#resizeObserver?.disconnect();
|
|
56
|
+
this.#resizeObserver = null;
|
|
57
|
+
this.unobserveViewport();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
3
61
|
// src/controllers/textarea_autosize_controller.ts
|
|
4
62
|
function px(value) {
|
|
5
63
|
const n = Number.parseFloat(value);
|
|
@@ -13,18 +71,69 @@ var TextareaAutosizeController = class extends Controller {
|
|
|
13
71
|
static actions = ["resize"];
|
|
14
72
|
static events = ["resize"];
|
|
15
73
|
#lastHeight = -1;
|
|
16
|
-
#
|
|
74
|
+
#lastWidth = -1;
|
|
75
|
+
#started = false;
|
|
76
|
+
#pending = false;
|
|
77
|
+
#fonts = null;
|
|
78
|
+
/**
|
|
79
|
+
* Content triggers arriving while the box is collapsed (`display: none`
|
|
80
|
+
* ancestors) are deferred: `scrollHeight` is 0 there, so measuring would
|
|
81
|
+
* clamp the kept height to the `minRows` floor and dispatch a bogus
|
|
82
|
+
* `resize`. The next report from a rendered box flushes the deferral.
|
|
83
|
+
*/
|
|
84
|
+
#remeasure = () => {
|
|
85
|
+
if (this.element.clientWidth === 0) {
|
|
86
|
+
this.#pending = true;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
17
89
|
this.resize();
|
|
18
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Re-measures when the content width changed (re-wrapping, including a
|
|
93
|
+
* horizontal padding change under `border-box`), the applied inline height
|
|
94
|
+
* was stripped (a Turbo morph syncing attributes from server HTML that has no
|
|
95
|
+
* `style`), or a content trigger was deferred while the box was collapsed.
|
|
96
|
+
* This instance's own height writes match none of these signals, so they
|
|
97
|
+
* cannot re-enter; an authored inline height (e.g. a user dragging the native
|
|
98
|
+
* resize handle) is left alone until the next content trigger. Reports from a
|
|
99
|
+
* still-collapsed box are skipped (nothing is measurable there).
|
|
100
|
+
*/
|
|
101
|
+
#layout = new LayoutObserver(() => {
|
|
102
|
+
if (this.element.clientWidth === 0) return;
|
|
103
|
+
const width = this.#contentWidth();
|
|
104
|
+
const stripped = this.element.style.height === "";
|
|
105
|
+
if (width === this.#lastWidth && !stripped && !this.#pending) return;
|
|
106
|
+
this.#lastWidth = width;
|
|
107
|
+
this.resize();
|
|
108
|
+
});
|
|
19
109
|
connect() {
|
|
20
|
-
this.element.
|
|
110
|
+
this.#lastHeight = Math.round(px(this.element.style.height));
|
|
111
|
+
this.#lastWidth = this.#contentWidth();
|
|
112
|
+
this.element.addEventListener("input", this.#remeasure);
|
|
113
|
+
this.element.addEventListener("change", this.#remeasure);
|
|
114
|
+
this.#layout.observe(this.element);
|
|
115
|
+
this.#bindFonts();
|
|
116
|
+
this.#started = true;
|
|
21
117
|
this.resize();
|
|
22
118
|
}
|
|
23
119
|
disconnect() {
|
|
24
|
-
this
|
|
120
|
+
this.#started = false;
|
|
121
|
+
this.element.removeEventListener("input", this.#remeasure);
|
|
122
|
+
this.element.removeEventListener("change", this.#remeasure);
|
|
123
|
+
this.#layout.disconnect();
|
|
124
|
+
this.#unbindFonts();
|
|
125
|
+
}
|
|
126
|
+
/** Re-clamps when application code or a Turbo morph changes `minRows`. */
|
|
127
|
+
minRowsValueChanged() {
|
|
128
|
+
if (this.#started) this.#remeasure();
|
|
129
|
+
}
|
|
130
|
+
/** Re-clamps when application code or a Turbo morph changes `maxRows`. */
|
|
131
|
+
maxRowsValueChanged() {
|
|
132
|
+
if (this.#started) this.#remeasure();
|
|
25
133
|
}
|
|
26
134
|
/** Re-measures the content and applies the clamped height. */
|
|
27
135
|
resize() {
|
|
136
|
+
this.#pending = false;
|
|
28
137
|
const el = this.element;
|
|
29
138
|
const style = window.getComputedStyle(el);
|
|
30
139
|
const lineHeight = this.#lineHeight(style);
|
|
@@ -58,6 +167,11 @@ var TextareaAutosizeController = class extends Controller {
|
|
|
58
167
|
this.dispatch("resize", { detail: { height, rows } });
|
|
59
168
|
}
|
|
60
169
|
}
|
|
170
|
+
/** Content-box width — the wrapping input, unaffected by this instance's height writes. */
|
|
171
|
+
#contentWidth() {
|
|
172
|
+
const style = window.getComputedStyle(this.element);
|
|
173
|
+
return Math.max(0, this.element.clientWidth - px(style.paddingLeft) - px(style.paddingRight));
|
|
174
|
+
}
|
|
61
175
|
/** Resolved line height, falling back to ~1.2× font-size when `normal`. */
|
|
62
176
|
#lineHeight(style) {
|
|
63
177
|
const lh = px(style.lineHeight);
|
|
@@ -65,6 +179,20 @@ var TextareaAutosizeController = class extends Controller {
|
|
|
65
179
|
const fontSize = px(style.fontSize);
|
|
66
180
|
return fontSize > 0 ? fontSize * 1.2 : 16;
|
|
67
181
|
}
|
|
182
|
+
/** Subscribes to font completion when the current engine exposes `document.fonts`. */
|
|
183
|
+
#bindFonts() {
|
|
184
|
+
const fonts = this.element.ownerDocument.fonts;
|
|
185
|
+
if (!fonts) return;
|
|
186
|
+
this.#fonts = fonts;
|
|
187
|
+
fonts.addEventListener("loadingdone", this.#remeasure);
|
|
188
|
+
fonts.addEventListener("loadingerror", this.#remeasure);
|
|
189
|
+
}
|
|
190
|
+
/** Removes the font completion subscriptions. */
|
|
191
|
+
#unbindFonts() {
|
|
192
|
+
this.#fonts?.removeEventListener("loadingdone", this.#remeasure);
|
|
193
|
+
this.#fonts?.removeEventListener("loadingerror", this.#remeasure);
|
|
194
|
+
this.#fonts = null;
|
|
195
|
+
}
|
|
68
196
|
};
|
|
69
197
|
|
|
70
198
|
export { TextareaAutosizeController };
|
|
@@ -23,15 +23,17 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
23
23
|
// src/utils/safe_storage.ts
|
|
24
24
|
function readLocalStorage(key) {
|
|
25
25
|
try {
|
|
26
|
-
return window.localStorage.getItem(key);
|
|
27
|
-
} catch {
|
|
28
|
-
return
|
|
26
|
+
return { ok: true, value: window.localStorage.getItem(key) };
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return { ok: false, error };
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
function writeLocalStorage(key, value) {
|
|
32
32
|
try {
|
|
33
33
|
window.localStorage.setItem(key, value);
|
|
34
|
-
|
|
34
|
+
return { ok: true, value: void 0 };
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return { ok: false, error };
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -169,8 +171,8 @@ var ThemeController = class extends Controller {
|
|
|
169
171
|
}
|
|
170
172
|
/** Reads a persisted, validated mode from `localStorage` (null when absent/blocked). */
|
|
171
173
|
#readStored() {
|
|
172
|
-
const
|
|
173
|
-
return isMode(value) ? value : null;
|
|
174
|
+
const result = readLocalStorage(this.storageKeyValue);
|
|
175
|
+
return result.ok && isMode(result.value) ? result.value : null;
|
|
174
176
|
}
|
|
175
177
|
/** Persists the mode, swallowing storage errors (private mode / quota). */
|
|
176
178
|
#writeStored(mode) {
|
|
@@ -24,7 +24,8 @@ function inheritsFieldsetDisabled(control) {
|
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
26
26
|
function canTakeFocus(element) {
|
|
27
|
-
if (element.closest("[hidden]")) return false;
|
|
27
|
+
if (element.closest("[hidden], [inert]")) return false;
|
|
28
|
+
if (element instanceof HTMLInputElement && element.type === "hidden") return false;
|
|
28
29
|
if (!("disabled" in element)) return true;
|
|
29
30
|
if (element.disabled) return false;
|
|
30
31
|
return !inheritsFieldsetDisabled(element);
|