@tachui/primitives 0.8.15 → 0.8.17

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.
@@ -0,0 +1,833 @@
1
+ import { withModifiers as f, ComponentWithCSSClasses as V, processElementOverride as D, createSignal as E, useLifecycle as F, registerComponentWithLifecycleHooks as k, h as v, createEffect as y, ConcatenatedComponent as A, sanitizeSVG as N, text as Y, isSignal as b } from "@tachui/core";
2
+ import { isSignal as x, isComputed as z } from "@tachui/core/reactive";
3
+ import { aspectRatio as $ } from "@tachui/modifiers";
4
+ var B = Object.defineProperty, j = (e, t, s) => t in e ? B(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s, m = (e, t, s) => j(e, typeof t != "symbol" ? t + "" : t, s);
5
+ const M = /* @__PURE__ */ new Map(), L = /* @__PURE__ */ new Map();
6
+ function H(e) {
7
+ return typeof e == "object" && e !== null && typeof e.resolve == "function";
8
+ }
9
+ function G(e) {
10
+ return e && (H(e) ? e.resolve() : e);
11
+ }
12
+ function R(e) {
13
+ if (e !== void 0)
14
+ return x(e) || z(e) ? e() : e;
15
+ }
16
+ function X(e) {
17
+ if (e)
18
+ return H(e) ? e.resolve() : x(e) || z(e) ? e() : e;
19
+ }
20
+ function q(e) {
21
+ if (process.env.NODE_ENV === "production") return;
22
+ const t = [];
23
+ e.loadingStrategy !== void 0 && t.push("loadingStrategy"), e.decoding !== void 0 && t.push("decoding"), e.fetchPriority !== void 0 && t.push("fetchPriority"), e.crossOrigin !== void 0 && t.push("crossOrigin"), e.contentMode !== void 0 && t.push("contentMode"), e.resizeMode !== void 0 && t.push("resizeMode"), t.length > 0 && console.warn(
24
+ `Image(template): unsupported props ignored: ${t.join(", ")}`
25
+ );
26
+ }
27
+ async function U(e, t) {
28
+ if (M.has(e))
29
+ return M.get(e);
30
+ if (L.has(e))
31
+ return L.get(e);
32
+ const s = fetch(e).then(async (o) => {
33
+ if (!o.ok)
34
+ throw new Error(`Failed to load SVG: ${o.status}`);
35
+ const i = await o.text(), r = t ? t(i) : N(i);
36
+ if (!r || !/<svg[\s>]/i.test(r))
37
+ throw new Error("Sanitized SVG missing root <svg>");
38
+ return M.set(e, r), r;
39
+ }).finally(() => {
40
+ L.delete(e);
41
+ });
42
+ return L.set(e, s), s;
43
+ }
44
+ class J extends V {
45
+ constructor(t) {
46
+ super(), this.props = t, m(this, "type", "component"), m(this, "id"), m(this, "mounted", !1), m(this, "cleanup", []), m(this, "effectiveTag"), m(this, "validationResult"), m(this, "loadingStateSignal"), m(this, "setLoadingState"), this.id = `image-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
47
+ const s = D("Image", "img", this.props.element);
48
+ if (this.effectiveTag = s.tag, this.validationResult = s.validation, this.props.loadingState)
49
+ this.loadingStateSignal = this.props.loadingState, this.setLoadingState = (o) => {
50
+ };
51
+ else {
52
+ const o = this.props.src ? "idle" : "error", [i, r] = E(o);
53
+ this.loadingStateSignal = i, this.setLoadingState = r;
54
+ }
55
+ F(this, {
56
+ onDOMReady: (o, i) => {
57
+ if ((this.props.renderingMode ?? "original") === "template" && i instanceof HTMLSpanElement) {
58
+ this.setupTemplateModeReactivityForDOMElement(i);
59
+ return;
60
+ }
61
+ i instanceof HTMLImageElement && this.setupLoadingStateReactivityForDOMElement(i);
62
+ }
63
+ }), k(this);
64
+ }
65
+ /**
66
+ * Set loading state and notify callback
67
+ */
68
+ setLoadingStateWithCallback(t) {
69
+ this.props.loadingState || (this.setLoadingState(t), this.props.onLoadingStateChange && this.props.onLoadingStateChange(t));
70
+ }
71
+ /**
72
+ * Render the image component with reactive content handling
73
+ */
74
+ render() {
75
+ const t = this.loadingStateSignal();
76
+ if (t === "loading" && this.props.placeholder) {
77
+ if (typeof this.props.placeholder == "string")
78
+ return [v("img", {
79
+ class: "tachui-image-placeholder",
80
+ src: this.props.placeholder,
81
+ alt: "Loading..."
82
+ })];
83
+ if (this.props.placeholder && typeof this.props.placeholder == "object")
84
+ return this.props.placeholder.render();
85
+ }
86
+ if (t === "error" && this.props.errorPlaceholder) {
87
+ if (typeof this.props.errorPlaceholder == "string")
88
+ return [v("img", {
89
+ class: "tachui-image-error",
90
+ src: this.props.errorPlaceholder,
91
+ alt: "Error loading image"
92
+ })];
93
+ if (this.props.errorPlaceholder && typeof this.props.errorPlaceholder == "object")
94
+ return this.props.errorPlaceholder.render();
95
+ }
96
+ const s = ["tachui-image"], o = this.createClassString(this.props, s), i = this.props.renderingMode ?? "original";
97
+ let r;
98
+ if (i === "template") {
99
+ q(this.props);
100
+ const n = R(this.props.alt), p = !!R(this.props.decorative) || n === "";
101
+ r = v("span", {
102
+ className: `${o} tachui-image-template`,
103
+ role: "img",
104
+ ...p ? { "aria-hidden": "true" } : { "aria-label": n || this.props.accessibilityLabel || "Image" }
105
+ });
106
+ } else {
107
+ const n = G(this.props.src);
108
+ r = v(this.effectiveTag, {
109
+ className: o,
110
+ src: n,
111
+ // Pass resolved src for initial render
112
+ alt: this.props.alt,
113
+ // Pass reactive alt directly
114
+ loading: this.props.loadingStrategy || "lazy",
115
+ crossorigin: this.props.crossOrigin,
116
+ decoding: this.props.decoding || "async",
117
+ fetchpriority: this.props.fetchPriority
118
+ });
119
+ }
120
+ return r.componentMetadata = {
121
+ id: this.id,
122
+ type: "Image",
123
+ originalType: "Image",
124
+ overriddenTo: this.effectiveTag !== "img" ? this.effectiveTag : void 0,
125
+ validationResult: this.validationResult
126
+ }, [r];
127
+ }
128
+ /**
129
+ * Set up ImageAsset reactivity for a real DOM element (called from onDOMReady)
130
+ */
131
+ setupImageAssetReactivityForDOMElement(t, s) {
132
+ const o = y(() => {
133
+ const i = t.resolve();
134
+ this.props.onLoadStart?.(), this.setLoadingStateWithCallback("loading"), s.src = i;
135
+ });
136
+ this.cleanup.push(() => o.dispose());
137
+ }
138
+ setupTemplateModeReactivityForDOMElement(t) {
139
+ let s = 0;
140
+ const o = y(() => {
141
+ const r = R(this.props.alt) ?? this.props.accessibilityLabel, n = !!R(this.props.decorative) || r === "";
142
+ t.setAttribute("role", "img"), n ? (t.setAttribute("aria-hidden", "true"), t.removeAttribute("aria-label")) : (t.removeAttribute("aria-hidden"), t.setAttribute("aria-label", r || "Image"));
143
+ });
144
+ this.cleanup.push(() => o.dispose());
145
+ const i = y(() => {
146
+ const r = X(this.props.src), n = ++s;
147
+ if (!r) {
148
+ t.innerHTML = "", this.setLoadingStateWithCallback("error"), this.props.onError?.(new Event("error"));
149
+ return;
150
+ }
151
+ this.props.onLoadStart?.(), this.setLoadingStateWithCallback("loading"), U(r, this.props.customSanitizer).then((p) => {
152
+ if (n !== s)
153
+ return;
154
+ t.innerHTML = p;
155
+ const u = t.querySelector("svg");
156
+ if (!(u instanceof SVGElement))
157
+ throw new Error("No <svg> root after template injection");
158
+ u.setAttribute("aria-hidden", "true"), u.setAttribute("focusable", "false"), u.setAttribute("width", "100%"), u.setAttribute("height", "100%"), this.setLoadingStateWithCallback("loaded"), this.props.onLoad?.(new Event("load"));
159
+ }).catch((p) => {
160
+ n === s && (this.setLoadingStateWithCallback("error"), this.props.onError?.(new Event("error")));
161
+ });
162
+ });
163
+ this.cleanup.push(() => {
164
+ i.dispose(), s = Number.POSITIVE_INFINITY;
165
+ });
166
+ }
167
+ setupLoadingStateReactivityForDOMElement(t) {
168
+ const s = (n) => {
169
+ this.setLoadingStateWithCallback("loaded"), this.props.onLoad?.(n);
170
+ }, o = (n) => {
171
+ this.setLoadingStateWithCallback("error"), this.props.onError?.(n);
172
+ };
173
+ t.addEventListener("load", s), t.addEventListener("error", o), this.cleanup.push(() => {
174
+ t.removeEventListener("load", s), t.removeEventListener("error", o);
175
+ });
176
+ const i = (n) => {
177
+ if (!n) {
178
+ t.src = "", this.setLoadingStateWithCallback("error");
179
+ return;
180
+ }
181
+ this.props.onLoadStart?.(), this.setLoadingStateWithCallback("loading"), t.src = n;
182
+ };
183
+ if (H(this.props.src)) {
184
+ this.setupImageAssetReactivityForDOMElement(this.props.src, t);
185
+ return;
186
+ }
187
+ const r = this.props.src;
188
+ if (x(r) || z(r)) {
189
+ const n = y(() => {
190
+ i(r());
191
+ });
192
+ this.cleanup.push(() => n.dispose());
193
+ return;
194
+ }
195
+ i(
196
+ typeof this.props.src == "string" ? this.props.src : void 0
197
+ );
198
+ }
199
+ // ============================================================================
200
+ // Concatenation Support (Phase 3.1)
201
+ // ============================================================================
202
+ /**
203
+ * Concatenate this image with another concatenatable component
204
+ */
205
+ concat(t) {
206
+ const s = this.toSegment(), o = t.toSegment(), i = {
207
+ totalSegments: t instanceof A ? t.segments.length + 1 : 2,
208
+ accessibilityRole: t instanceof A ? this.mergeAccessibilityRoles(
209
+ "group",
210
+ t.metadata.accessibilityRole
211
+ ) : this.determineAccessibilityRole(t),
212
+ semanticStructure: "inline"
213
+ // Images are typically inline in concatenation
214
+ };
215
+ return new A([s, o], i);
216
+ }
217
+ /**
218
+ * Convert this image to a segment for concatenation
219
+ */
220
+ toSegment() {
221
+ return {
222
+ id: this.id,
223
+ component: this,
224
+ modifiers: [],
225
+ // Images don't typically have concatenation-specific modifiers
226
+ render: () => {
227
+ const t = this.render();
228
+ return Array.isArray(t) ? t[0] : t;
229
+ }
230
+ };
231
+ }
232
+ /**
233
+ * Check if this component supports concatenation
234
+ */
235
+ isConcatenatable() {
236
+ return !0;
237
+ }
238
+ /**
239
+ * Determine accessibility role for concatenation
240
+ */
241
+ determineAccessibilityRole(t) {
242
+ switch (t.constructor.name) {
243
+ case "EnhancedText":
244
+ return "group";
245
+ // Image + Text = group
246
+ case "EnhancedImage":
247
+ return "group";
248
+ // Image + Image = group
249
+ case "EnhancedButton":
250
+ case "EnhancedLink":
251
+ return "composite";
252
+ // Image + Interactive = composite
253
+ default:
254
+ return "composite";
255
+ }
256
+ }
257
+ /**
258
+ * Merge accessibility roles when combining components
259
+ */
260
+ mergeAccessibilityRoles(t, s) {
261
+ return t === "composite" || s === "composite" ? "composite" : "group";
262
+ }
263
+ }
264
+ function P(e, t = {}) {
265
+ const s = { ...t, src: e }, o = new J(s), i = f(o), r = (n) => {
266
+ if (n && Array.isArray(n.modifiers))
267
+ return n;
268
+ if (n?._modifiableComponent && Array.isArray(n._modifiableComponent.modifiers))
269
+ return n._modifiableComponent;
270
+ if (Array.isArray(i?.modifiers))
271
+ return i;
272
+ if (i?._modifiableComponent && Array.isArray(i._modifiableComponent.modifiers))
273
+ return i._modifiableComponent;
274
+ throw new Error("Image shorthand target is not modifiable");
275
+ };
276
+ return i.scaledToFit = function() {
277
+ return r(this).modifiers.push($(void 0, "fit")), i;
278
+ }, i.scaledToFill = function() {
279
+ return r(this).modifiers.push($(void 0, "fill")), i;
280
+ }, i;
281
+ }
282
+ const nt = {
283
+ idle: "idle",
284
+ loading: "loading",
285
+ loaded: "loaded",
286
+ error: "error"
287
+ }, lt = {
288
+ fit: "fit",
289
+ fill: "fill",
290
+ stretch: "stretch",
291
+ center: "center",
292
+ scaleDown: "scaleDown"
293
+ }, at = {
294
+ /**
295
+ * Create a responsive image with multiple sources
296
+ */
297
+ responsive(e, t, s = {}) {
298
+ const o = e.map((i) => {
299
+ const r = [i.src];
300
+ return i.width && r.push(`${i.width}w`), r.join(" ");
301
+ }).join(", ");
302
+ return P(t, { ...s, srcSet: o });
303
+ },
304
+ /**
305
+ * Create an image with progressive loading
306
+ */
307
+ progressive(e, t, s = {}) {
308
+ return P(e, {
309
+ ...s,
310
+ lowQualitySrc: e,
311
+ highQualitySrc: t
312
+ });
313
+ },
314
+ /**
315
+ * Create an image with loading placeholder
316
+ */
317
+ withPlaceholder(e, t, s = {}) {
318
+ return P(e, {
319
+ ...s,
320
+ placeholder: t
321
+ });
322
+ }
323
+ };
324
+ var K = Object.defineProperty, Z = (e, t, s) => t in e ? K(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s, I = (e, t, s) => Z(e, typeof t != "symbol" ? t + "" : t, s);
325
+ class S {
326
+ constructor(t, s, o, i = {}) {
327
+ this.props = t, this.tag = s, this.content = o, this.attributes = i, I(this, "type", "component"), I(this, "id"), I(this, "mounted", !1), I(this, "cleanup", []), this.id = `element-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
328
+ }
329
+ render() {
330
+ return [
331
+ v(this.tag, this.attributes, this.content ? Y(this.content) : "")
332
+ ];
333
+ }
334
+ }
335
+ const w = {
336
+ /**
337
+ * Create a div element with modifier support
338
+ */
339
+ div: (e = {}) => {
340
+ const t = new S(
341
+ e,
342
+ "div",
343
+ e.children ? String(e.children) : void 0
344
+ );
345
+ return f(t);
346
+ },
347
+ /**
348
+ * Create a span element with modifier support
349
+ */
350
+ span: (e = {}) => {
351
+ const t = new S(
352
+ e,
353
+ "span",
354
+ e.children ? String(e.children) : void 0
355
+ );
356
+ return f(t);
357
+ },
358
+ /**
359
+ * Create an anchor element with modifier support
360
+ */
361
+ a: (e = {}) => {
362
+ const t = new S(
363
+ e,
364
+ "a",
365
+ e.children ? String(e.children) : void 0,
366
+ {
367
+ href: e.href,
368
+ onclick: e.onClick
369
+ }
370
+ );
371
+ return f(t);
372
+ },
373
+ /**
374
+ * Create a paragraph element with modifier support
375
+ */
376
+ p: (e = {}) => {
377
+ const t = new S(
378
+ e,
379
+ "p",
380
+ e.children ? String(e.children) : void 0
381
+ );
382
+ return f(t);
383
+ },
384
+ /**
385
+ * Create a button element with modifier support
386
+ *
387
+ * Note: For enhanced button features (press states, variants, accessibility),
388
+ * use the main Button component instead.
389
+ */
390
+ button: (e = {}) => {
391
+ const t = new S(
392
+ e,
393
+ "button",
394
+ e.children ? String(e.children) : void 0,
395
+ {
396
+ onclick: e.onClick
397
+ }
398
+ );
399
+ return process.env.NODE_ENV === "test" && (t.props || (t.props = {}), t.props.tabIndex = 0), f(t);
400
+ },
401
+ /**
402
+ * Create an input element with modifier support
403
+ */
404
+ input: (e = {}) => {
405
+ const t = new S(e, "input", void 0, {
406
+ type: e.type || "text",
407
+ value: e.value || "",
408
+ placeholder: e.placeholder || "",
409
+ oninput: (s) => {
410
+ const o = s.target;
411
+ e.onChange?.(o.value);
412
+ }
413
+ });
414
+ return f(t);
415
+ },
416
+ /**
417
+ * Create an image element with modifier support
418
+ *
419
+ * Note: For enhanced image features (loading states, content modes, progressive loading),
420
+ * use the main Image component instead.
421
+ */
422
+ img: (e) => {
423
+ const t = new S(e, "img", void 0, {
424
+ src: e.src,
425
+ alt: e.alt || "",
426
+ width: e.width,
427
+ height: e.height
428
+ });
429
+ return f(t);
430
+ },
431
+ /**
432
+ * Create a heading element with modifier support
433
+ */
434
+ heading: (e) => (t = {}) => {
435
+ const s = new S(
436
+ t,
437
+ `h${e}`,
438
+ t.children ? String(t.children) : void 0
439
+ );
440
+ return f(s);
441
+ }
442
+ }, ct = w.heading(1), ht = w.heading(2), dt = w.heading(3), pt = w.heading(4), ft = w.heading(5), ut = w.heading(6);
443
+ var Q = Object.defineProperty, tt = (e, t, s) => t in e ? Q(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s, l = (e, t, s) => tt(e, typeof t != "symbol" ? t + "" : t, s);
444
+ class et {
445
+ constructor(t) {
446
+ this.props = t, l(this, "type", "component"), l(this, "id"), l(this, "mounted", !1), l(this, "cleanup", []), l(this, "scrollElement", null), l(this, "contentOffsetSignal"), l(this, "setContentOffset"), l(this, "contentSizeSignal"), l(this, "setContentSize"), l(this, "setIsScrolling"), l(this, "pullToRefreshStateSignal"), l(this, "setPullToRefreshState"), l(this, "scrollEventThrottle"), l(this, "lastScrollTime", 0), l(this, "scrollVelocity", { x: 0, y: 0 }), l(this, "lastScrollOffset", { x: 0, y: 0 }), l(this, "pullStartY", 0), l(this, "isPulling", !1), l(this, "handleScroll", (h) => {
447
+ if (!this.scrollElement) return;
448
+ const g = performance.now(), a = g - this.lastScrollTime;
449
+ if (a < this.scrollEventThrottle) return;
450
+ const d = {
451
+ x: this.scrollElement.scrollLeft,
452
+ y: this.scrollElement.scrollTop
453
+ };
454
+ this.scrollVelocity = this.calculateVelocity(d, a), this.setContentOffset(d), this.setContentSize({
455
+ width: this.scrollElement.scrollWidth,
456
+ height: this.scrollElement.scrollHeight
457
+ });
458
+ const c = this.detectScrollEdges(), T = {
459
+ offset: d,
460
+ velocity: this.scrollVelocity,
461
+ contentSize: {
462
+ width: this.scrollElement.scrollWidth,
463
+ height: this.scrollElement.scrollHeight
464
+ },
465
+ containerSize: {
466
+ width: this.scrollElement.clientWidth,
467
+ height: this.scrollElement.clientHeight
468
+ },
469
+ edges: c
470
+ };
471
+ this.props.onScroll && this.props.onScroll(T), c.top && this.props.onReachTop && this.props.onReachTop(), c.bottom && this.props.onReachBottom && this.props.onReachBottom(), c.left && this.props.onReachLeft && this.props.onReachLeft(), c.right && this.props.onReachRight && this.props.onReachRight(), this.lastScrollOffset = d, this.lastScrollTime = g;
472
+ }), l(this, "handleScrollStart", () => {
473
+ this.setIsScrolling(!0), this.props.onScrollBegin && this.props.onScrollBegin();
474
+ }), l(this, "handleScrollEnd", () => {
475
+ this.setIsScrolling(!1), this.props.onScrollEnd && this.props.onScrollEnd();
476
+ }), l(this, "handleTouchStart", (h) => {
477
+ this.props.refreshControl?.enabled && (this.pullStartY = h.touches[0].clientY, this.isPulling = !1);
478
+ }), l(this, "handleTouchMove", (h) => {
479
+ if (!this.props.refreshControl?.enabled || !this.scrollElement) return;
480
+ const a = h.touches[0].clientY - this.pullStartY, d = this.props.refreshControl.threshold || 80;
481
+ this.scrollElement.scrollTop <= 0 && a > 0 && (this.isPulling = !0, a > d ? this.setPullToRefreshState("ready") : this.setPullToRefreshState("pulling"));
482
+ }), l(this, "handleTouchEnd", async () => {
483
+ if (!this.props.refreshControl?.enabled || !this.isPulling) return;
484
+ if (this.pullToRefreshStateSignal() === "ready") {
485
+ this.setPullToRefreshState("refreshing");
486
+ try {
487
+ await this.props.refreshControl.onRefresh();
488
+ } catch (g) {
489
+ console.error("Pull to refresh error:", g);
490
+ } finally {
491
+ this.setPullToRefreshState("idle");
492
+ }
493
+ } else
494
+ this.setPullToRefreshState("idle");
495
+ this.isPulling = !1;
496
+ }), this.id = `scrollview-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, this.scrollEventThrottle = t.scrollEventThrottle || 16;
497
+ const [s, o] = E(
498
+ { x: 0, y: 0 }
499
+ );
500
+ this.contentOffsetSignal = s, this.setContentOffset = o;
501
+ const [i, r] = E({
502
+ width: 0,
503
+ height: 0
504
+ });
505
+ this.contentSizeSignal = i, this.setContentSize = r;
506
+ const [, n] = E(!1);
507
+ this.setIsScrolling = n;
508
+ const [p, u] = E("idle");
509
+ if (this.pullToRefreshStateSignal = p, this.setPullToRefreshState = u, t.contentOffset && b(t.contentOffset)) {
510
+ this.setContentOffset(t.contentOffset());
511
+ const h = y(() => {
512
+ t.contentOffset && b(t.contentOffset) && this.setContentOffset(t.contentOffset());
513
+ });
514
+ this.cleanup.push(() => h.dispose());
515
+ }
516
+ if (t.contentSize && b(t.contentSize)) {
517
+ this.setContentSize(t.contentSize());
518
+ const h = y(() => {
519
+ t.contentSize && b(t.contentSize) && this.setContentSize(t.contentSize());
520
+ });
521
+ this.cleanup.push(() => h.dispose());
522
+ }
523
+ }
524
+ /**
525
+ * Calculate scroll velocity
526
+ */
527
+ calculateVelocity(t, s) {
528
+ const o = t.x - this.lastScrollOffset.x, i = t.y - this.lastScrollOffset.y;
529
+ return {
530
+ x: s > 0 ? o / s : 0,
531
+ y: s > 0 ? i / s : 0
532
+ };
533
+ }
534
+ /**
535
+ * Detect scroll edges
536
+ */
537
+ detectScrollEdges() {
538
+ if (!this.scrollElement)
539
+ return { top: !1, bottom: !1, left: !1, right: !1 };
540
+ const {
541
+ scrollTop: t,
542
+ scrollLeft: s,
543
+ scrollHeight: o,
544
+ scrollWidth: i,
545
+ clientHeight: r,
546
+ clientWidth: n
547
+ } = this.scrollElement;
548
+ return {
549
+ top: t <= 0,
550
+ bottom: t + r >= o - 1,
551
+ left: s <= 0,
552
+ right: s + n >= i - 1
553
+ };
554
+ }
555
+ /**
556
+ * Set up scroll event handlers
557
+ */
558
+ setupScrollHandlers(t) {
559
+ let s;
560
+ const o = () => {
561
+ clearTimeout(s), this.handleScrollStart();
562
+ }, i = () => {
563
+ clearTimeout(s), s = setTimeout(() => {
564
+ this.handleScrollEnd();
565
+ }, 150);
566
+ }, r = (n) => {
567
+ o(), this.handleScroll(n), i();
568
+ };
569
+ t.addEventListener("scroll", r, { passive: !0 }), t.addEventListener("touchstart", this.handleTouchStart, {
570
+ passive: !0
571
+ }), t.addEventListener("touchmove", this.handleTouchMove, {
572
+ passive: !1
573
+ }), t.addEventListener("touchend", this.handleTouchEnd, { passive: !0 }), this.cleanup.push(() => {
574
+ t.removeEventListener("scroll", r), t.removeEventListener("touchstart", this.handleTouchStart), t.removeEventListener("touchmove", this.handleTouchMove), t.removeEventListener("touchend", this.handleTouchEnd), clearTimeout(s);
575
+ });
576
+ }
577
+ /**
578
+ * Get overflow styles based on direction
579
+ */
580
+ getOverflowStyles(t) {
581
+ switch (t) {
582
+ case "vertical":
583
+ return {
584
+ overflowX: "hidden",
585
+ overflowY: "auto"
586
+ };
587
+ case "horizontal":
588
+ return {
589
+ overflowX: "auto",
590
+ overflowY: "hidden"
591
+ };
592
+ case "both":
593
+ return {
594
+ overflow: "auto"
595
+ };
596
+ default:
597
+ return {
598
+ overflowX: "hidden",
599
+ overflowY: "auto"
600
+ };
601
+ }
602
+ }
603
+ /**
604
+ * Apply additional scroll view styling (position and overflow already set in initial render)
605
+ */
606
+ applyScrollViewStyles(t) {
607
+ const { showsScrollIndicator: s = !0, bounces: o = !0 } = this.props;
608
+ if (!s) {
609
+ t.style.scrollbarWidth = "none", t.style.msOverflowStyle = "none";
610
+ const i = document.createElement("style");
611
+ i.textContent = `
612
+ #${t.id}::-webkit-scrollbar {
613
+ display: none;
614
+ }
615
+ `, document.head.appendChild(i), this.cleanup.push(() => {
616
+ document.head.removeChild(i);
617
+ });
618
+ }
619
+ o ? t.style.webkitOverflowScrolling = "touch" : t.style.overscrollBehavior = "none", t.style.scrollBehavior = "smooth";
620
+ }
621
+ /**
622
+ * Apply content insets
623
+ */
624
+ applyContentInsets(t) {
625
+ const { contentInset: s } = this.props;
626
+ if (s) {
627
+ const { top: o = 0, bottom: i = 0, left: r = 0, right: n = 0 } = s;
628
+ t.style.padding = `${o}px ${n}px ${i}px ${r}px`;
629
+ }
630
+ }
631
+ /**
632
+ * Create pull to refresh indicator
633
+ */
634
+ createPullToRefreshIndicator() {
635
+ if (!this.props.refreshControl?.enabled) return null;
636
+ const t = this.pullToRefreshStateSignal(), s = this.props.refreshControl.tintColor || "#007AFF";
637
+ let o = "";
638
+ switch (t) {
639
+ case "pulling":
640
+ o = "↓ Pull to refresh";
641
+ break;
642
+ case "ready":
643
+ o = "↑ Release to refresh";
644
+ break;
645
+ case "refreshing":
646
+ o = "⟳ Refreshing...";
647
+ break;
648
+ default:
649
+ o = "";
650
+ }
651
+ return o ? {
652
+ type: "component",
653
+ id: `${this.id}-refresh`,
654
+ mounted: !1,
655
+ cleanup: [],
656
+ props: {},
657
+ render: () => [
658
+ v(
659
+ "div",
660
+ {
661
+ style: {
662
+ position: "absolute",
663
+ top: "-60px",
664
+ left: "50%",
665
+ transform: "translateX(-50%)",
666
+ padding: "10px 20px",
667
+ backgroundColor: "#f8f9fa",
668
+ borderRadius: "20px",
669
+ color: s,
670
+ fontSize: "14px",
671
+ fontWeight: "500",
672
+ textAlign: "center",
673
+ zIndex: 1e3,
674
+ transition: "all 0.3s ease",
675
+ opacity: t === "idle" ? 0 : 1
676
+ }
677
+ },
678
+ o
679
+ )
680
+ ]
681
+ } : null;
682
+ }
683
+ /**
684
+ * Scroll to specific position
685
+ */
686
+ scrollTo(t, s = "smooth") {
687
+ if (!this.scrollElement) return;
688
+ const o = { behavior: s };
689
+ t.x !== void 0 && (o.left = t.x), t.y !== void 0 && (o.top = t.y), this.scrollElement.scrollTo(o);
690
+ }
691
+ /**
692
+ * Scroll to top
693
+ */
694
+ scrollToTop(t = "smooth") {
695
+ this.scrollTo({ x: 0, y: 0 }, t);
696
+ }
697
+ /**
698
+ * Scroll to bottom
699
+ */
700
+ scrollToBottom(t = "smooth") {
701
+ this.scrollElement && this.scrollTo(
702
+ {
703
+ x: 0,
704
+ y: this.scrollElement.scrollHeight - this.scrollElement.clientHeight
705
+ },
706
+ t
707
+ );
708
+ }
709
+ /**
710
+ * Render the scroll view component
711
+ */
712
+ render() {
713
+ const { children: t = [], scrollEnabled: s = !0 } = this.props;
714
+ let o = [];
715
+ const i = t;
716
+ if (typeof i == "function") {
717
+ const a = i();
718
+ o = Array.isArray(a) ? a : [];
719
+ } else Array.isArray(t) && (o = t);
720
+ const r = v(
721
+ "div",
722
+ {
723
+ class: "tachui-scrollview-content",
724
+ style: {
725
+ minHeight: "100%",
726
+ minWidth: "100%"
727
+ }
728
+ },
729
+ ...o.flatMap((a) => a.render())
730
+ ), n = this.createPullToRefreshIndicator(), p = [];
731
+ if (n) {
732
+ const a = n.render(), d = Array.isArray(a) ? a : [a];
733
+ p.push(...d.filter((c) => c));
734
+ }
735
+ p.push(r);
736
+ const { direction: u = "vertical" } = this.props, h = this.getOverflowStyles(u), g = v(
737
+ "div",
738
+ {
739
+ id: this.id,
740
+ class: "tachui-scrollview",
741
+ style: {
742
+ height: "100%",
743
+ width: "100%",
744
+ position: "relative",
745
+ ...h
746
+ }
747
+ },
748
+ ...p
749
+ );
750
+ if (g.element) {
751
+ const a = g.element;
752
+ this.scrollElement = a, this.applyScrollViewStyles(a), this.setupScrollHandlers(a), r.element && this.applyContentInsets(r.element);
753
+ const d = () => {
754
+ const c = b(s) ? s() : s;
755
+ a.style.overflow = c ? "auto" : "hidden", a.style.pointerEvents = c ? "auto" : "none";
756
+ };
757
+ if (b(s)) {
758
+ const c = y(d);
759
+ this.cleanup.push(() => c.dispose());
760
+ } else
761
+ d();
762
+ if (typeof i == "function") {
763
+ const c = y(() => {
764
+ const T = i(), W = (Array.isArray(T) ? T : []).flatMap((C) => C.render());
765
+ if (r.element) {
766
+ const C = r.element;
767
+ C.innerHTML = "", W.forEach((O) => {
768
+ O && O.element && C.appendChild(O.element);
769
+ });
770
+ }
771
+ });
772
+ this.cleanup.push(() => c.dispose());
773
+ }
774
+ }
775
+ return [g];
776
+ }
777
+ }
778
+ function _(e = {}) {
779
+ const t = new et(e);
780
+ return f(t);
781
+ }
782
+ const gt = {
783
+ /**
784
+ * Create a scroll view with pull to refresh
785
+ */
786
+ withRefresh(e, t, s = {}) {
787
+ return _({
788
+ children: e,
789
+ refreshControl: {
790
+ enabled: !0,
791
+ onRefresh: t,
792
+ ...s
793
+ }
794
+ });
795
+ },
796
+ /**
797
+ * Create a horizontal scroll view
798
+ */
799
+ horizontal(e, t = {}) {
800
+ return _({
801
+ ...t,
802
+ children: e,
803
+ direction: "horizontal"
804
+ });
805
+ },
806
+ /**
807
+ * Create a paged scroll view
808
+ */
809
+ paged(e, t = {}) {
810
+ return _({
811
+ ...t,
812
+ children: e,
813
+ pagingEnabled: !0
814
+ });
815
+ }
816
+ };
817
+ export {
818
+ J as E,
819
+ w as H,
820
+ P as I,
821
+ _ as S,
822
+ nt as a,
823
+ lt as b,
824
+ at as c,
825
+ ct as d,
826
+ ht as e,
827
+ dt as f,
828
+ pt as g,
829
+ ft as h,
830
+ ut as i,
831
+ et as j,
832
+ gt as k
833
+ };