@marianmeres/stuic 3.25.0 → 3.26.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.
@@ -72,6 +72,12 @@
72
72
  /** Custom class for active item */
73
73
  classItemActive?: string;
74
74
 
75
+ /** Show prev/next arrow buttons overlaid on left/right edges */
76
+ arrows?: boolean;
77
+
78
+ /** Custom class for arrow buttons */
79
+ classArrow?: string;
80
+
75
81
  /** Skip all default styling */
76
82
  unstyled?: boolean;
77
83
 
@@ -90,6 +96,11 @@
90
96
  import { ItemCollection } from "@marianmeres/item-collection";
91
97
  import { twMerge } from "../../utils/tw-merge.js";
92
98
  import Thc from "../Thc/Thc.svelte";
99
+ import Button from "../Button/Button.svelte";
100
+ import {
101
+ iconArrowLeft as iconPrevious,
102
+ iconArrowRight as iconNext,
103
+ } from "../../icons/index.js";
93
104
 
94
105
  let {
95
106
  items,
@@ -111,6 +122,8 @@
111
122
  classTrack,
112
123
  classItem,
113
124
  classItemActive,
125
+ arrows = false,
126
+ classArrow,
114
127
  unstyled = false,
115
128
  el = $bindable(),
116
129
  onActiveChange,
@@ -367,5 +380,47 @@
367
380
  </div>
368
381
  {/each}
369
382
  </div>
383
+
384
+ {#if arrows && coll.size > 1}
385
+ <div
386
+ class="absolute inset-0 flex items-center justify-between pointer-events-none"
387
+ >
388
+ <Button
389
+ class={twMerge(
390
+ "stuic-carousel-arrow pointer-events-auto p-0! ml-4",
391
+ classArrow
392
+ )}
393
+ onclick={() => {
394
+ coll.setActivePrevious();
395
+ scrollActiveIntoView();
396
+ }}
397
+ type="button"
398
+ disabled={!loop && coll.activeIndex === 0}
399
+ aspect1
400
+ variant="soft"
401
+ roundedFull
402
+ >
403
+ {@html iconPrevious({ size: 24 })}
404
+ </Button>
405
+
406
+ <Button
407
+ class={twMerge(
408
+ "stuic-carousel-arrow pointer-events-auto p-0! mr-4",
409
+ classArrow
410
+ )}
411
+ onclick={() => {
412
+ coll.setActiveNext();
413
+ scrollActiveIntoView();
414
+ }}
415
+ type="button"
416
+ disabled={!loop && coll.activeIndex === coll.size - 1}
417
+ aspect1
418
+ variant="soft"
419
+ roundedFull
420
+ >
421
+ {@html iconNext({ size: 24 })}
422
+ </Button>
423
+ </div>
424
+ {/if}
370
425
  </div>
371
426
  {/if}
@@ -51,6 +51,10 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
51
51
  classItem?: string;
52
52
  /** Custom class for active item */
53
53
  classItemActive?: string;
54
+ /** Show prev/next arrow buttons overlaid on left/right edges */
55
+ arrows?: boolean;
56
+ /** Custom class for arrow buttons */
57
+ classArrow?: string;
54
58
  /** Skip all default styling */
55
59
  unstyled?: boolean;
56
60
  /** Bindable element reference */
@@ -1,3 +1,4 @@
1
+ import { validatePhoneNumber } from "../../Input/phone-validation.js";
1
2
  // ====================================================================
2
3
  // Default price formatter
3
4
  // ====================================================================
@@ -28,6 +29,9 @@ export function validateCustomerForm(data, t) {
28
29
  const emailError = validateEmail(data.email, t);
29
30
  if (emailError)
30
31
  errors.push({ field: "email", message: emailError });
32
+ const phoneError = validatePhoneNumber(data.phone);
33
+ if (phoneError)
34
+ errors.push({ field: "phone", message: phoneError });
31
35
  return errors;
32
36
  }
33
37
  // ====================================================================
@@ -52,6 +56,9 @@ export function validateAddress(address, prefix, t) {
52
56
  });
53
57
  }
54
58
  }
59
+ const phoneError = validatePhoneNumber(address.phone);
60
+ if (phoneError)
61
+ errors.push({ field: `${prefix}.phone`, message: phoneError });
55
62
  return errors;
56
63
  }
57
64
  // ====================================================================
@@ -201,12 +201,12 @@
201
201
  localNumber = _localNumber;
202
202
  }
203
203
 
204
- // Compose full value from parts
204
+ // Compose full value from parts (strip only formatting chars, keep letters so they fail validation)
205
205
  function composeValue(): string {
206
- const digits = _localNumber.replace(/\D/g, "");
207
- if (!digits) return "";
208
- if (selectedCountry) return `+${selectedCountry.dialCode}${digits}`;
209
- return digits;
206
+ const cleaned = _localNumber.replace(/[\s\-().\/]/g, "");
207
+ if (!cleaned) return "";
208
+ if (selectedCountry) return `+${selectedCountry.dialCode}${cleaned}`;
209
+ return cleaned;
210
210
  }
211
211
 
212
212
  // Sync internal -> external value
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.25.0",
3
+ "version": "3.26.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",