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
|
@@ -161,6 +161,8 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
161
161
|
var TabindexLoan = class {
|
|
162
162
|
#value;
|
|
163
163
|
#lent = /* @__PURE__ */ new Set();
|
|
164
|
+
/** Returns live loans before Turbo can copy them into its page snapshot. */
|
|
165
|
+
#beforeCache = new BeforeCacheReset(() => this.returnAll());
|
|
164
166
|
/**
|
|
165
167
|
* @param value - the `tabindex` to lend. `"-1"` (the default) is
|
|
166
168
|
* programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
|
|
@@ -174,6 +176,7 @@ var TabindexLoan = class {
|
|
|
174
176
|
if (element.hasAttribute("tabindex")) return;
|
|
175
177
|
element.setAttribute("tabindex", this.#value);
|
|
176
178
|
this.#lent.add(element);
|
|
179
|
+
this.#beforeCache.activate();
|
|
177
180
|
}
|
|
178
181
|
/** Takes back every loan whose value is still the one that was lent. */
|
|
179
182
|
returnAll() {
|
|
@@ -181,6 +184,7 @@ var TabindexLoan = class {
|
|
|
181
184
|
if (element.getAttribute("tabindex") === this.#value) element.removeAttribute("tabindex");
|
|
182
185
|
}
|
|
183
186
|
this.#lent.clear();
|
|
187
|
+
this.#beforeCache.deactivate();
|
|
184
188
|
}
|
|
185
189
|
};
|
|
186
190
|
|
|
@@ -190,7 +194,7 @@ var INDEX = "data-stimeo--overflow-menu-index";
|
|
|
190
194
|
var BOUNDARY = "data-stimeo--overflow-menu-boundary";
|
|
191
195
|
var SAVED_ROLE = "data-stimeo--overflow-menu-role";
|
|
192
196
|
var SAVED_TABINDEX = "data-stimeo--overflow-menu-tabindex";
|
|
193
|
-
var SAVED_MENU_TARGET = "data-stimeo--overflow-menu-menu
|
|
197
|
+
var SAVED_MENU_TARGET = "data-stimeo--overflow-menu-saved-menu";
|
|
194
198
|
var MENU_TARGET = "data-stimeo--menu-target";
|
|
195
199
|
var BOOKKEEPING = [BANKED, INDEX, SAVED_ROLE, SAVED_TABINDEX, SAVED_MENU_TARGET];
|
|
196
200
|
var OverflowMenuController = class extends Controller {
|
|
@@ -19,10 +19,41 @@ function canTakeFocus(element) {
|
|
|
19
19
|
return !inheritsFieldsetDisabled(element);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// src/utils/before_cache_reset.ts
|
|
23
|
+
var BeforeCacheReset = class _BeforeCacheReset {
|
|
24
|
+
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
25
|
+
static #subscribers = /* @__PURE__ */ new Set();
|
|
26
|
+
/** The shared listener; installed while at least one instance is subscribed. */
|
|
27
|
+
static #onBeforeCache = () => {
|
|
28
|
+
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
29
|
+
};
|
|
30
|
+
#rewind;
|
|
31
|
+
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
32
|
+
constructor(rewind) {
|
|
33
|
+
this.#rewind = rewind;
|
|
34
|
+
}
|
|
35
|
+
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
36
|
+
activate() {
|
|
37
|
+
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
38
|
+
_BeforeCacheReset.#subscribers.add(this);
|
|
39
|
+
if (first) {
|
|
40
|
+
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
44
|
+
deactivate() {
|
|
45
|
+
_BeforeCacheReset.#subscribers.delete(this);
|
|
46
|
+
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
47
|
+
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
22
51
|
// src/utils/tabindex_loan.ts
|
|
23
52
|
var TabindexLoan = class {
|
|
24
53
|
#value;
|
|
25
54
|
#lent = /* @__PURE__ */ new Set();
|
|
55
|
+
/** Returns live loans before Turbo can copy them into its page snapshot. */
|
|
56
|
+
#beforeCache = new BeforeCacheReset(() => this.returnAll());
|
|
26
57
|
/**
|
|
27
58
|
* @param value - the `tabindex` to lend. `"-1"` (the default) is
|
|
28
59
|
* programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
|
|
@@ -36,6 +67,7 @@ var TabindexLoan = class {
|
|
|
36
67
|
if (element.hasAttribute("tabindex")) return;
|
|
37
68
|
element.setAttribute("tabindex", this.#value);
|
|
38
69
|
this.#lent.add(element);
|
|
70
|
+
this.#beforeCache.activate();
|
|
39
71
|
}
|
|
40
72
|
/** Takes back every loan whose value is still the one that was lent. */
|
|
41
73
|
returnAll() {
|
|
@@ -43,6 +75,7 @@ var TabindexLoan = class {
|
|
|
43
75
|
if (element.getAttribute("tabindex") === this.#value) element.removeAttribute("tabindex");
|
|
44
76
|
}
|
|
45
77
|
this.#lent.clear();
|
|
78
|
+
this.#beforeCache.deactivate();
|
|
46
79
|
}
|
|
47
80
|
};
|
|
48
81
|
|
|
@@ -132,7 +165,11 @@ var PaginationController = class _PaginationController extends Controller {
|
|
|
132
165
|
if (!Object.is(page, this.pageValue)) this.pageValue = page;
|
|
133
166
|
this.#render();
|
|
134
167
|
}
|
|
135
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* Syncs `aria-current` on the page buttons and the prev/next `disabled` state.
|
|
170
|
+
*
|
|
171
|
+
* @stimeoRenderRoot
|
|
172
|
+
*/
|
|
136
173
|
#render() {
|
|
137
174
|
const page = this.#page;
|
|
138
175
|
for (const button of this.pageTargets) {
|
|
@@ -55,8 +55,23 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
// src/utils/string_list.ts
|
|
59
|
+
function parseStringList(raw, fallback = []) {
|
|
60
|
+
const text = raw.trim();
|
|
61
|
+
if (text.length === 0) return [...fallback];
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(text);
|
|
65
|
+
} catch {
|
|
66
|
+
return [...fallback];
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(parsed)) return [...fallback];
|
|
69
|
+
return parsed.filter((entry) => typeof entry === "string");
|
|
70
|
+
}
|
|
71
|
+
|
|
58
72
|
// src/controllers/password_strength_controller.ts
|
|
59
73
|
var CLASS_PATTERNS = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/];
|
|
74
|
+
var DEFAULT_LEVELS = ["weak", "fair", "good", "strong"];
|
|
60
75
|
var LENGTH_MILESTONES = [8, 12, 16];
|
|
61
76
|
var MAX_POINTS = LENGTH_MILESTONES.length + (CLASS_PATTERNS.length - 1);
|
|
62
77
|
var STRENGTH_BANDS = ["weak", "fair", "good", "strong"];
|
|
@@ -64,7 +79,10 @@ var PasswordStrengthController = class _PasswordStrengthController extends Contr
|
|
|
64
79
|
static targets = ["input", "meter", "label"];
|
|
65
80
|
static values = {
|
|
66
81
|
minScore: { type: Number, default: 0 },
|
|
67
|
-
|
|
82
|
+
// A JSON list read through `parseStringList` rather than Stimulus's `Array`
|
|
83
|
+
// type: that reader throws out of the value observer before any callback
|
|
84
|
+
// runs, so one malformed attribute would stop the meter connecting.
|
|
85
|
+
levels: { type: String, default: "" }
|
|
68
86
|
};
|
|
69
87
|
static actions = ["evaluate"];
|
|
70
88
|
static events = ["change"];
|
|
@@ -90,7 +108,7 @@ var PasswordStrengthController = class _PasswordStrengthController extends Contr
|
|
|
90
108
|
*/
|
|
91
109
|
#update(options = {}) {
|
|
92
110
|
const password = this.hasInputTarget ? this.inputTarget.value : "";
|
|
93
|
-
const labels = this.levelsValue;
|
|
111
|
+
const labels = parseStringList(this.levelsValue, DEFAULT_LEVELS);
|
|
94
112
|
const max = labels.length;
|
|
95
113
|
const score = this.#score(password, max);
|
|
96
114
|
const label = score > 0 ? labels[score - 1] ?? "" : "";
|
|
@@ -126,7 +144,7 @@ var PasswordStrengthController = class _PasswordStrengthController extends Contr
|
|
|
126
144
|
this.#toggle("data-strength", band, band.length > 0);
|
|
127
145
|
this.#toggle("data-below-min", "true", score > 0 && score < this.minScoreValue);
|
|
128
146
|
const ratio = max > 0 ? score / max : 0;
|
|
129
|
-
this.element.style.setProperty("--stimeo
|
|
147
|
+
this.element.style.setProperty("--stimeo--password-strength", String(ratio));
|
|
130
148
|
}
|
|
131
149
|
#writeLabel(label) {
|
|
132
150
|
if (this.hasLabelTarget) this.labelTarget.textContent = label;
|
|
@@ -55,8 +55,23 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
// src/utils/string_list.ts
|
|
59
|
+
function parseStringList(raw, fallback = []) {
|
|
60
|
+
const text = raw.trim();
|
|
61
|
+
if (text.length === 0) return [...fallback];
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(text);
|
|
65
|
+
} catch {
|
|
66
|
+
return [...fallback];
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(parsed)) return [...fallback];
|
|
69
|
+
return parsed.filter((entry) => typeof entry === "string");
|
|
70
|
+
}
|
|
71
|
+
|
|
58
72
|
// src/controllers/persist_controller.ts
|
|
59
73
|
var NON_VALUE_TYPES = /* @__PURE__ */ new Set(["file", "submit", "reset", "button", "image"]);
|
|
74
|
+
var DEFAULT_EXCLUDE = ["password"];
|
|
60
75
|
var STORAGE_PREFIX = "stimeo--persist:";
|
|
61
76
|
var OCCURRENCE_SEP = "\0";
|
|
62
77
|
var PersistController = class extends Controller {
|
|
@@ -64,7 +79,10 @@ var PersistController = class extends Controller {
|
|
|
64
79
|
static values = {
|
|
65
80
|
key: { type: String, default: "" },
|
|
66
81
|
debounce: { type: Number, default: 400 },
|
|
67
|
-
|
|
82
|
+
// A JSON list read through `parseStringList` rather than Stimulus's `Array`
|
|
83
|
+
// type: that reader throws out of the value observer before any callback
|
|
84
|
+
// runs, so one malformed attribute would stop the controller connecting.
|
|
85
|
+
exclude: { type: String, default: "" },
|
|
68
86
|
clearOn: { type: String, default: "" }
|
|
69
87
|
};
|
|
70
88
|
static actions = ["clear"];
|
|
@@ -208,15 +226,16 @@ var PersistController = class extends Controller {
|
|
|
208
226
|
/** The fields to persist: `field` targets, or the element's named controls. */
|
|
209
227
|
#fields() {
|
|
210
228
|
const candidates = this.hasFieldTarget ? this.fieldTargets : Array.from(this.element.querySelectorAll("input, textarea, select"));
|
|
211
|
-
|
|
229
|
+
const excluded = parseStringList(this.excludeValue, DEFAULT_EXCLUDE);
|
|
230
|
+
return candidates.filter((field) => this.#persistable(field, excluded));
|
|
212
231
|
}
|
|
213
232
|
/** Whether a field carries a restorable, non-excluded value. */
|
|
214
|
-
#persistable(field) {
|
|
233
|
+
#persistable(field, excluded) {
|
|
215
234
|
if (this.#keyOf(field) === null) return false;
|
|
216
235
|
const type = field instanceof HTMLInputElement ? field.type : "";
|
|
217
236
|
if (NON_VALUE_TYPES.has(type)) return false;
|
|
218
|
-
if (
|
|
219
|
-
if (field.name.length > 0 &&
|
|
237
|
+
if (excluded.includes(type)) return false;
|
|
238
|
+
if (field.name.length > 0 && excluded.includes(field.name)) return false;
|
|
220
239
|
return true;
|
|
221
240
|
}
|
|
222
241
|
/** A stable storage sub-key for a field (its name, else id). */
|
|
@@ -12,6 +12,16 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
12
12
|
var DetachGate = class _DetachGate {
|
|
13
13
|
/** Set while a probe is queued, waiting for a reconnect to cancel it. */
|
|
14
14
|
#pending = false;
|
|
15
|
+
/**
|
|
16
|
+
* True while a probe is queued — the last disconnect was ambiguous and no
|
|
17
|
+
* reconnect has cancelled it yet. Read it from `connect()` to tell the
|
|
18
|
+
* reconnect half of an in-page move from a first connect: a controller whose
|
|
19
|
+
* initialisation restarts a measurement (a min-duration floor, an elapsed
|
|
20
|
+
* counter) must skip it for the move, where nothing actually restarted.
|
|
21
|
+
*/
|
|
22
|
+
get pending() {
|
|
23
|
+
return this.#pending;
|
|
24
|
+
}
|
|
15
25
|
/**
|
|
16
26
|
* True when the disconnect is definitely a real detach — the element left
|
|
17
27
|
* the document, or `data-controller` no longer lists the identifier. False
|
|
@@ -6,6 +6,16 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
6
6
|
var DetachGate = class _DetachGate {
|
|
7
7
|
/** Set while a probe is queued, waiting for a reconnect to cancel it. */
|
|
8
8
|
#pending = false;
|
|
9
|
+
/**
|
|
10
|
+
* True while a probe is queued — the last disconnect was ambiguous and no
|
|
11
|
+
* reconnect has cancelled it yet. Read it from `connect()` to tell the
|
|
12
|
+
* reconnect half of an in-page move from a first connect: a controller whose
|
|
13
|
+
* initialisation restarts a measurement (a min-duration floor, an elapsed
|
|
14
|
+
* counter) must skip it for the move, where nothing actually restarted.
|
|
15
|
+
*/
|
|
16
|
+
get pending() {
|
|
17
|
+
return this.#pending;
|
|
18
|
+
}
|
|
9
19
|
/**
|
|
10
20
|
* True when the disconnect is definitely a real detach — the element left
|
|
11
21
|
* the document, or `data-controller` no longer lists the identifier. False
|
|
@@ -153,20 +153,24 @@ var ProgressController = class extends Controller {
|
|
|
153
153
|
get #ratio() {
|
|
154
154
|
return rangeFraction(this.valueValue, this.minValue, this.maxValue);
|
|
155
155
|
}
|
|
156
|
-
/**
|
|
156
|
+
/**
|
|
157
|
+
* Reflects value/range/indeterminate onto ARIA, `data-state`, and the ratio.
|
|
158
|
+
*
|
|
159
|
+
* @stimeoRenderRoot
|
|
160
|
+
*/
|
|
157
161
|
#render() {
|
|
158
162
|
this.element.setAttribute("aria-valuemin", String(this.minValue));
|
|
159
163
|
this.element.setAttribute("aria-valuemax", String(this.maxValue));
|
|
160
164
|
if (this.indeterminateValue) {
|
|
161
165
|
this.element.removeAttribute("aria-valuenow");
|
|
162
166
|
this.#clearOwnValueText();
|
|
163
|
-
this.element.style.removeProperty("--stimeo
|
|
167
|
+
this.element.style.removeProperty("--stimeo--progress-ratio");
|
|
164
168
|
this.element.setAttribute("data-state", "indeterminate");
|
|
165
169
|
return;
|
|
166
170
|
}
|
|
167
171
|
const value = this.#clamp(this.valueValue);
|
|
168
172
|
this.element.setAttribute("aria-valuenow", String(value));
|
|
169
|
-
this.element.style.setProperty("--stimeo
|
|
173
|
+
this.element.style.setProperty("--stimeo--progress-ratio", String(this.#ratio));
|
|
170
174
|
this.element.setAttribute("data-state", "determinate");
|
|
171
175
|
this.#applyValueText(value);
|
|
172
176
|
}
|