@vespera-ui/vue 0.1.0 → 0.3.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.
package/dist/index.cjs CHANGED
@@ -20,21 +20,41 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ Accordion: () => Accordion,
23
24
  Alert: () => Alert,
25
+ Avatar: () => Avatar,
26
+ AvatarGroup: () => AvatarGroup,
24
27
  Badge: () => Badge,
28
+ Banner: () => Banner,
29
+ Breadcrumb: () => Breadcrumb,
25
30
  Button: () => Button,
26
31
  Card: () => Card,
27
32
  CardHead: () => CardHead,
28
33
  Checkbox: () => Checkbox,
34
+ CircularProgress: () => CircularProgress,
35
+ DescriptionList: () => DescriptionList,
29
36
  Divider: () => Divider,
37
+ EmptyState: () => EmptyState,
30
38
  Field: () => Field,
31
39
  IconButton: () => IconButton,
32
40
  Input: () => Input,
33
41
  Kbd: () => Kbd,
42
+ NativeSelect: () => NativeSelect,
43
+ Pagination: () => Pagination,
44
+ Progress: () => Progress,
45
+ Radio: () => Radio,
46
+ RadioGroup: () => RadioGroup,
47
+ Segmented: () => Segmented,
48
+ Skeleton: () => Skeleton,
49
+ Slider: () => Slider,
34
50
  Spinner: () => Spinner,
51
+ Stat: () => Stat,
52
+ Stepper: () => Stepper,
35
53
  Switch: () => Switch,
54
+ Tabs: () => Tabs,
36
55
  Tag: () => Tag,
37
- Textarea: () => Textarea
56
+ Textarea: () => Textarea,
57
+ Timeline: () => Timeline
38
58
  });
39
59
  module.exports = __toCommonJS(index_exports);
40
60
  var import_vue = require("vue");
@@ -251,22 +271,803 @@ var Checkbox = (0, import_vue.defineComponent)({
251
271
  );
252
272
  }
253
273
  });
274
+ var optValue = (o) => typeof o === "string" ? o : o.value;
275
+ var optLabel = (o) => typeof o === "string" ? o : o.label;
276
+ var Radio = (0, import_vue.defineComponent)({
277
+ name: "VspRadio",
278
+ props: {
279
+ checked: Boolean,
280
+ label: { type: String, default: void 0 },
281
+ sub: { type: String, default: void 0 }
282
+ },
283
+ emits: ["select"],
284
+ setup(props, { emit }) {
285
+ return () => (0, import_vue.h)(
286
+ "label",
287
+ {
288
+ class: "ui-opt",
289
+ onClick: (e) => {
290
+ e.preventDefault();
291
+ emit("select");
292
+ }
293
+ },
294
+ [
295
+ (0, import_vue.h)("span", { class: cx("ui-radio-dot", props.checked && "on") }),
296
+ (0, import_vue.h)("span", null, [
297
+ (0, import_vue.h)("span", null, props.label),
298
+ props.sub ? (0, import_vue.h)("span", { class: "ui-opt-sub" }, props.sub) : null
299
+ ])
300
+ ]
301
+ );
302
+ }
303
+ });
304
+ var RadioGroup = (0, import_vue.defineComponent)({
305
+ name: "VspRadioGroup",
306
+ props: {
307
+ modelValue: { type: String, default: void 0 },
308
+ options: { type: Array, default: () => [] }
309
+ },
310
+ emits: ["update:modelValue"],
311
+ setup(props, { emit }) {
312
+ return () => (0, import_vue.h)(
313
+ "div",
314
+ { style: { display: "flex", flexDirection: "column", gap: "12px" } },
315
+ props.options.map(
316
+ (o) => (0, import_vue.h)(Radio, {
317
+ key: optValue(o),
318
+ label: optLabel(o),
319
+ sub: typeof o === "object" ? o.sub : void 0,
320
+ checked: props.modelValue === optValue(o),
321
+ onSelect: () => emit("update:modelValue", optValue(o))
322
+ })
323
+ )
324
+ );
325
+ }
326
+ });
327
+ var Slider = (0, import_vue.defineComponent)({
328
+ name: "VspSlider",
329
+ props: {
330
+ modelValue: { type: Number, default: 0 },
331
+ min: { type: Number, default: 0 },
332
+ max: { type: Number, default: 100 },
333
+ step: { type: Number, default: 1 }
334
+ },
335
+ emits: ["update:modelValue"],
336
+ setup(props, { emit }) {
337
+ return () => (0, import_vue.h)("input", {
338
+ type: "range",
339
+ class: "ui-slider",
340
+ value: props.modelValue,
341
+ min: props.min,
342
+ max: props.max,
343
+ step: props.step,
344
+ onInput: (e) => emit("update:modelValue", Number(e.target.value))
345
+ });
346
+ }
347
+ });
348
+ var NativeSelect = (0, import_vue.defineComponent)({
349
+ name: "VspNativeSelect",
350
+ props: {
351
+ modelValue: { type: String, default: void 0 },
352
+ options: { type: Array, default: () => [] }
353
+ },
354
+ emits: ["update:modelValue"],
355
+ setup(props, { emit, attrs }) {
356
+ return () => (0, import_vue.h)(
357
+ "select",
358
+ {
359
+ class: "ui-select",
360
+ value: props.modelValue,
361
+ onChange: (e) => emit("update:modelValue", e.target.value),
362
+ ...attrs
363
+ },
364
+ props.options.map(
365
+ (o) => (0, import_vue.h)("option", { key: optValue(o), value: optValue(o) }, optLabel(o))
366
+ )
367
+ );
368
+ }
369
+ });
370
+ var px = (v) => typeof v === "number" ? `${v}px` : v;
371
+ var Progress = (0, import_vue.defineComponent)({
372
+ name: "VspProgress",
373
+ props: {
374
+ value: { type: Number, default: 0 },
375
+ tone: { type: String, default: void 0 },
376
+ height: { type: Number, default: 6 }
377
+ },
378
+ setup(props) {
379
+ return () => (0, import_vue.h)("div", { class: "meter", style: { height: px(props.height) } }, [
380
+ (0, import_vue.h)("i", {
381
+ style: {
382
+ width: `${Math.min(100, props.value)}%`,
383
+ background: props.tone,
384
+ transition: "width .3s"
385
+ }
386
+ })
387
+ ]);
388
+ }
389
+ });
390
+ var Skeleton = (0, import_vue.defineComponent)({
391
+ name: "VspSkeleton",
392
+ props: {
393
+ w: { type: [String, Number], default: "100%" },
394
+ h: { type: [String, Number], default: 12 },
395
+ r: { type: Number, default: 7 }
396
+ },
397
+ setup(props) {
398
+ return () => (0, import_vue.h)("div", {
399
+ class: "skel",
400
+ style: { width: px(props.w), height: px(props.h), borderRadius: px(props.r) }
401
+ });
402
+ }
403
+ });
404
+ var initialsOf = (name) => name.split(" ").map((s) => s.charAt(0)).slice(0, 2).join("").toUpperCase();
405
+ var Avatar = (0, import_vue.defineComponent)({
406
+ name: "VspAvatar",
407
+ props: {
408
+ name: { type: String, required: true },
409
+ hue: { type: Number, default: 0 },
410
+ size: { type: Number, default: 34 }
411
+ },
412
+ setup(props) {
413
+ return () => (0, import_vue.h)(
414
+ "span",
415
+ {
416
+ class: "vsp-avatar",
417
+ style: {
418
+ width: px(props.size),
419
+ height: px(props.size),
420
+ fontSize: px(props.size * 0.38),
421
+ background: `linear-gradient(140deg, oklch(0.62 0.16 ${props.hue}), oklch(0.55 0.17 ${(props.hue + 50) % 360}))`
422
+ }
423
+ },
424
+ initialsOf(props.name)
425
+ );
426
+ }
427
+ });
428
+ var AvatarGroup = (0, import_vue.defineComponent)({
429
+ name: "VspAvatarGroup",
430
+ props: {
431
+ people: { type: Array, default: () => [] },
432
+ max: { type: Number, default: 4 },
433
+ size: { type: Number, default: 32 }
434
+ },
435
+ setup(props) {
436
+ return () => {
437
+ const shown = props.people.slice(0, props.max);
438
+ const extra = props.people.length - shown.length;
439
+ return (0, import_vue.h)("div", { style: { display: "flex", alignItems: "center" } }, [
440
+ ...shown.map(
441
+ (p, i) => (0, import_vue.h)(
442
+ "span",
443
+ {
444
+ key: i,
445
+ style: {
446
+ marginLeft: i ? "-10px" : "0",
447
+ border: "2px solid var(--surface-1)",
448
+ borderRadius: "50%",
449
+ position: "relative",
450
+ zIndex: shown.length - i
451
+ }
452
+ },
453
+ [(0, import_vue.h)(Avatar, { name: p.name, hue: p.hue ?? 0, size: props.size })]
454
+ )
455
+ ),
456
+ extra > 0 ? (0, import_vue.h)(
457
+ "span",
458
+ {
459
+ style: {
460
+ marginLeft: "-10px",
461
+ width: px(props.size),
462
+ height: px(props.size),
463
+ borderRadius: "50%",
464
+ display: "grid",
465
+ placeItems: "center",
466
+ background: "var(--surface-3)",
467
+ border: "2px solid var(--surface-1)",
468
+ fontSize: px(props.size * 0.34),
469
+ fontWeight: 700,
470
+ color: "var(--text-dim)"
471
+ }
472
+ },
473
+ `+${extra}`
474
+ ) : null
475
+ ]);
476
+ };
477
+ }
478
+ });
479
+ var Segmented = (0, import_vue.defineComponent)({
480
+ name: "VspSegmented",
481
+ props: {
482
+ modelValue: { type: String, default: void 0 },
483
+ options: { type: Array, default: () => [] }
484
+ },
485
+ emits: ["update:modelValue"],
486
+ setup(props, { emit }) {
487
+ return () => (0, import_vue.h)(
488
+ "div",
489
+ { class: "ui-seg" },
490
+ props.options.map(
491
+ (o) => (0, import_vue.h)(
492
+ "button",
493
+ {
494
+ key: o,
495
+ type: "button",
496
+ class: cx(props.modelValue === o && "on"),
497
+ onClick: () => emit("update:modelValue", o)
498
+ },
499
+ o
500
+ )
501
+ )
502
+ );
503
+ }
504
+ });
505
+ var ICON_PATHS = { chevL: "M15 18l-6-6 6-6", chevR: "M9 18l6-6-6-6", check: "M20 6L9 17l-5-5" };
506
+ var svgIcon = (d, size = 14) => (0, import_vue.h)(
507
+ "svg",
508
+ {
509
+ viewBox: "0 0 24 24",
510
+ width: size,
511
+ height: size,
512
+ fill: "none",
513
+ stroke: "currentColor",
514
+ "stroke-width": 2,
515
+ "stroke-linecap": "round",
516
+ "stroke-linejoin": "round"
517
+ },
518
+ [(0, import_vue.h)("path", { d })]
519
+ );
520
+ var Tabs = (0, import_vue.defineComponent)({
521
+ name: "VspTabs",
522
+ props: {
523
+ tabs: { type: Array, default: () => [] },
524
+ modelValue: { type: String, default: void 0 }
525
+ },
526
+ emits: ["update:modelValue"],
527
+ setup(props, { emit, slots }) {
528
+ return () => (0, import_vue.h)("div", { class: "ui-tabs", style: { alignItems: "center" } }, [
529
+ ...props.tabs.map((t) => {
530
+ const id = typeof t === "string" ? t : t.value;
531
+ const label = typeof t === "string" ? t : t.label;
532
+ const count = typeof t === "object" ? t.count : void 0;
533
+ return (0, import_vue.h)(
534
+ "button",
535
+ {
536
+ key: id,
537
+ type: "button",
538
+ class: cx("ui-tab", props.modelValue === id && "on"),
539
+ onClick: () => emit("update:modelValue", id)
540
+ },
541
+ [
542
+ label,
543
+ count != null ? (0, import_vue.h)("span", { class: "badge badge-muted", style: { marginLeft: "7px" } }, count) : null
544
+ ]
545
+ );
546
+ }),
547
+ slots.right ? (0, import_vue.h)("div", { style: { flex: 1 } }) : null,
548
+ slots.right?.()
549
+ ]);
550
+ }
551
+ });
552
+ var Breadcrumb = (0, import_vue.defineComponent)({
553
+ name: "VspBreadcrumb",
554
+ props: { items: { type: Array, default: () => [] } },
555
+ setup(props) {
556
+ return () => (0, import_vue.h)(
557
+ "nav",
558
+ { style: { display: "flex", alignItems: "center", gap: "7px", fontSize: "12.5px" } },
559
+ props.items.flatMap((it, i) => {
560
+ const last = i === props.items.length - 1;
561
+ return [
562
+ i > 0 ? (0, import_vue.h)(
563
+ "span",
564
+ { key: `s${i}`, style: { color: "var(--text-faint)", display: "flex" } },
565
+ [svgIcon(ICON_PATHS.chevR, 13)]
566
+ ) : null,
567
+ (0, import_vue.h)(
568
+ "span",
569
+ {
570
+ key: i,
571
+ style: {
572
+ color: last ? "var(--text)" : "var(--text-dim)",
573
+ fontWeight: last ? 600 : 500
574
+ }
575
+ },
576
+ it
577
+ )
578
+ ];
579
+ })
580
+ );
581
+ }
582
+ });
583
+ var Pagination = (0, import_vue.defineComponent)({
584
+ name: "VspPagination",
585
+ props: {
586
+ modelValue: { type: Number, default: 0 },
587
+ pages: { type: Number, default: 1 }
588
+ },
589
+ emits: ["update:modelValue"],
590
+ setup(props, { emit }) {
591
+ return () => {
592
+ const page = props.modelValue;
593
+ const nums = [];
594
+ for (let i = 0; i < props.pages; i++) {
595
+ if (i === 0 || i === props.pages - 1 || Math.abs(i - page) <= 1) nums.push(i);
596
+ else if (nums[nums.length - 1] !== "\u2026") nums.push("\u2026");
597
+ }
598
+ return (0, import_vue.h)("div", { style: { display: "flex", gap: "4px", alignItems: "center" } }, [
599
+ (0, import_vue.h)(
600
+ "button",
601
+ {
602
+ type: "button",
603
+ class: "btn btn-ghost btn-sm",
604
+ disabled: page === 0,
605
+ "aria-label": "Previous page",
606
+ onClick: () => emit("update:modelValue", page - 1)
607
+ },
608
+ [svgIcon(ICON_PATHS.chevL)]
609
+ ),
610
+ ...nums.map(
611
+ (n, i) => n === "\u2026" ? (0, import_vue.h)(
612
+ "span",
613
+ {
614
+ key: `g${i}`,
615
+ class: "mono",
616
+ style: { padding: "0 6px", color: "var(--text-faint)" }
617
+ },
618
+ "\u2026"
619
+ ) : (0, import_vue.h)(
620
+ "button",
621
+ {
622
+ key: n,
623
+ type: "button",
624
+ class: cx("btn", "btn-sm", n === page ? "btn-primary" : "btn-subtle"),
625
+ style: { minWidth: "32px", padding: 0 },
626
+ onClick: () => emit("update:modelValue", n)
627
+ },
628
+ n + 1
629
+ )
630
+ ),
631
+ (0, import_vue.h)(
632
+ "button",
633
+ {
634
+ type: "button",
635
+ class: "btn btn-ghost btn-sm",
636
+ disabled: page >= props.pages - 1,
637
+ "aria-label": "Next page",
638
+ onClick: () => emit("update:modelValue", page + 1)
639
+ },
640
+ [svgIcon(ICON_PATHS.chevR)]
641
+ )
642
+ ]);
643
+ };
644
+ }
645
+ });
646
+ var Stepper = (0, import_vue.defineComponent)({
647
+ name: "VspStepper",
648
+ props: {
649
+ steps: { type: Array, default: () => [] },
650
+ current: { type: Number, default: 0 }
651
+ },
652
+ setup(props) {
653
+ return () => (0, import_vue.h)(
654
+ "div",
655
+ { class: "ui-steps" },
656
+ props.steps.flatMap((s, i) => [
657
+ i > 0 ? (0, import_vue.h)("div", { key: `b${i}`, class: cx("ui-step-bar", i <= props.current && "done") }) : null,
658
+ (0, import_vue.h)(
659
+ "div",
660
+ {
661
+ key: i,
662
+ class: cx(
663
+ "ui-step",
664
+ i < props.current && "done",
665
+ i === props.current && "active",
666
+ i > props.current && "pending"
667
+ )
668
+ },
669
+ [
670
+ (0, import_vue.h)("span", { class: "ui-step-dot" }, [
671
+ i < props.current ? svgIcon(ICON_PATHS.check) : i + 1
672
+ ]),
673
+ (0, import_vue.h)("span", { class: "ui-step-label" }, s)
674
+ ]
675
+ )
676
+ ])
677
+ );
678
+ }
679
+ });
680
+ var CircularProgress = (0, import_vue.defineComponent)({
681
+ name: "VspCircularProgress",
682
+ props: {
683
+ value: { type: Number, default: 0 },
684
+ size: { type: Number, default: 76 },
685
+ thickness: { type: Number, default: 7 },
686
+ color: { type: String, default: "var(--accent)" },
687
+ label: { type: String, default: void 0 }
688
+ },
689
+ setup(props) {
690
+ return () => {
691
+ const r = (props.size - props.thickness) / 2;
692
+ const circ = 2 * Math.PI * r;
693
+ return (0, import_vue.h)(
694
+ "div",
695
+ { style: { position: "relative", width: px(props.size), height: px(props.size) } },
696
+ [
697
+ (0, import_vue.h)(
698
+ "svg",
699
+ { width: props.size, height: props.size, style: { transform: "rotate(-90deg)" } },
700
+ [
701
+ (0, import_vue.h)("circle", {
702
+ cx: props.size / 2,
703
+ cy: props.size / 2,
704
+ r,
705
+ fill: "none",
706
+ stroke: "var(--surface-3)",
707
+ "stroke-width": props.thickness
708
+ }),
709
+ (0, import_vue.h)("circle", {
710
+ cx: props.size / 2,
711
+ cy: props.size / 2,
712
+ r,
713
+ fill: "none",
714
+ stroke: props.color,
715
+ "stroke-width": props.thickness,
716
+ "stroke-linecap": "round",
717
+ "stroke-dasharray": circ,
718
+ "stroke-dashoffset": circ * (1 - Math.min(100, props.value) / 100),
719
+ style: { transition: "stroke-dashoffset .5s cubic-bezier(.3,.7,.3,1)" }
720
+ })
721
+ ]
722
+ ),
723
+ (0, import_vue.h)(
724
+ "div",
725
+ {
726
+ class: "tnum",
727
+ style: {
728
+ position: "absolute",
729
+ inset: 0,
730
+ display: "grid",
731
+ placeItems: "center",
732
+ fontWeight: 800,
733
+ fontSize: px(props.size * 0.24)
734
+ }
735
+ },
736
+ props.label ?? `${Math.round(props.value)}%`
737
+ )
738
+ ]
739
+ );
740
+ };
741
+ }
742
+ });
743
+ var Stat = (0, import_vue.defineComponent)({
744
+ name: "VspStat",
745
+ props: {
746
+ label: { type: String, default: void 0 },
747
+ value: { type: String, default: void 0 },
748
+ delta: { type: String, default: void 0 },
749
+ deltaDir: { type: String, default: "up" },
750
+ tone: { type: String, default: "var(--accent)" }
751
+ },
752
+ setup(props, { slots }) {
753
+ return () => (0, import_vue.h)(
754
+ "div",
755
+ { class: "card card-pad", style: { display: "flex", alignItems: "center", gap: "13px" } },
756
+ [
757
+ slots.icon ? (0, import_vue.h)(
758
+ "span",
759
+ {
760
+ style: {
761
+ width: "38px",
762
+ height: "38px",
763
+ borderRadius: "var(--r-sm)",
764
+ flexShrink: 0,
765
+ display: "grid",
766
+ placeItems: "center",
767
+ background: `color-mix(in oklab, ${props.tone} 14%, transparent)`,
768
+ color: props.tone
769
+ }
770
+ },
771
+ slots.icon()
772
+ ) : null,
773
+ (0, import_vue.h)("div", { style: { minWidth: 0 } }, [
774
+ (0, import_vue.h)("div", { class: "eyebrow" }, props.label),
775
+ (0, import_vue.h)(
776
+ "div",
777
+ { style: { display: "flex", alignItems: "baseline", gap: "8px", marginTop: "3px" } },
778
+ [
779
+ (0, import_vue.h)(
780
+ "span",
781
+ {
782
+ class: "tnum",
783
+ style: { fontSize: "22px", fontWeight: 800, letterSpacing: "-.02em" }
784
+ },
785
+ props.value
786
+ ),
787
+ props.delta != null ? (0, import_vue.h)(
788
+ "span",
789
+ {
790
+ class: cx("badge", props.deltaDir === "up" ? "badge-pos" : "badge-neg"),
791
+ style: { padding: "1px 6px" }
792
+ },
793
+ [
794
+ svgIcon(
795
+ props.deltaDir === "up" ? "M12 19V5M5 12l7-7 7 7" : "M12 5v14M5 12l7 7 7-7",
796
+ 10
797
+ ),
798
+ props.delta
799
+ ]
800
+ ) : null
801
+ ]
802
+ )
803
+ ])
804
+ ]
805
+ );
806
+ }
807
+ });
808
+ var clockSvg = (size = 14) => (0, import_vue.h)(
809
+ "svg",
810
+ {
811
+ viewBox: "0 0 24 24",
812
+ width: size,
813
+ height: size,
814
+ fill: "none",
815
+ stroke: "currentColor",
816
+ "stroke-width": 2,
817
+ "stroke-linecap": "round",
818
+ "stroke-linejoin": "round"
819
+ },
820
+ [(0, import_vue.h)("circle", { cx: 12, cy: 12, r: 9 }), (0, import_vue.h)("path", { d: "M12 7v5l3 2" })]
821
+ );
822
+ var TL_TONE = {
823
+ pos: "var(--success)",
824
+ neg: "var(--danger)",
825
+ warn: "var(--warning)",
826
+ info: "var(--accent)"
827
+ };
828
+ var Timeline = (0, import_vue.defineComponent)({
829
+ name: "VspTimeline",
830
+ props: { items: { type: Array, default: () => [] } },
831
+ setup(props) {
832
+ return () => (0, import_vue.h)(
833
+ "div",
834
+ { class: "ui-tl" },
835
+ props.items.map((it, i) => {
836
+ const c = it.tone ? TL_TONE[it.tone] : void 0;
837
+ return (0, import_vue.h)("div", { key: i, class: "ui-tl-item" }, [
838
+ (0, import_vue.h)(
839
+ "span",
840
+ {
841
+ class: "ui-tl-dot",
842
+ style: c ? {
843
+ background: `color-mix(in oklab, ${c} 14%, transparent)`,
844
+ color: c,
845
+ borderColor: `color-mix(in oklab, ${c} 30%, transparent)`
846
+ } : void 0
847
+ },
848
+ [clockSvg()]
849
+ ),
850
+ (0, import_vue.h)("div", { class: "ui-tl-body" }, [
851
+ (0, import_vue.h)(
852
+ "div",
853
+ {
854
+ style: { display: "flex", alignItems: "baseline", gap: "8px", flexWrap: "wrap" }
855
+ },
856
+ [
857
+ (0, import_vue.h)("span", { style: { fontWeight: 600, fontSize: "13.5px" } }, it.title),
858
+ it.time ? (0, import_vue.h)("span", { class: "eyebrow", style: { marginLeft: "auto" } }, it.time) : null
859
+ ]
860
+ ),
861
+ it.body ? (0, import_vue.h)(
862
+ "div",
863
+ { style: { fontSize: "12.5px", color: "var(--text-dim)", marginTop: "3px" } },
864
+ it.body
865
+ ) : null
866
+ ])
867
+ ]);
868
+ })
869
+ );
870
+ }
871
+ });
872
+ var DescriptionList = (0, import_vue.defineComponent)({
873
+ name: "VspDescriptionList",
874
+ props: { items: { type: Array, default: () => [] } },
875
+ setup(props) {
876
+ return () => (0, import_vue.h)(
877
+ "dl",
878
+ { class: "ui-dl" },
879
+ props.items.flatMap(([k, v], i) => {
880
+ const last = i === props.items.length - 1 ? "last" : "";
881
+ return [
882
+ (0, import_vue.h)("dt", { key: `k${i}`, class: last }, k),
883
+ (0, import_vue.h)("dd", { key: `v${i}`, class: last }, v)
884
+ ];
885
+ })
886
+ );
887
+ }
888
+ });
889
+ var BANNER_ICON = {
890
+ info: "M12 3l1.6 5L19 9.6l-5 1.6L12 16l-1.6-4.8L5 9.6l5.4-1.6z",
891
+ warn: "M18 8a6 6 0 00-12 0c0 7-3 9-3 9h18s-3-2-3-9",
892
+ accent: "M13 2L3 14h9l-1 8 10-12h-9l1-8z"
893
+ };
894
+ var X_PATH = "M18 6L6 18M6 6l12 12";
895
+ var INBOX_PATH = "M22 12h-6l-2 3h-4l-2-3H2M5.45 5.11L2 12v6a2 2 0 002 2h16a2 2 0 002-2v-6l-3.45-6.89A2 2 0 0016.76 4H7.24a2 2 0 00-1.79 1.11z";
896
+ var Banner = (0, import_vue.defineComponent)({
897
+ name: "VspBanner",
898
+ props: {
899
+ tone: { type: String, default: "info" },
900
+ dismissible: Boolean
901
+ },
902
+ emits: ["dismiss"],
903
+ setup(props, { slots, emit }) {
904
+ return () => (0, import_vue.h)("div", { class: cx("ui-banner", props.tone) }, [
905
+ slots.icon ? slots.icon() : svgIcon(BANNER_ICON[props.tone], 18),
906
+ (0, import_vue.h)("div", { style: { flex: 1, fontSize: "13px", fontWeight: 500 } }, slots.default?.()),
907
+ slots.action?.(),
908
+ props.dismissible ? (0, import_vue.h)(
909
+ "button",
910
+ {
911
+ type: "button",
912
+ class: "ui-banner-x",
913
+ "aria-label": "Dismiss",
914
+ onClick: () => emit("dismiss")
915
+ },
916
+ [svgIcon(X_PATH, 15)]
917
+ ) : null
918
+ ]);
919
+ }
920
+ });
921
+ var EmptyState = (0, import_vue.defineComponent)({
922
+ name: "VspEmptyState",
923
+ props: {
924
+ title: { type: String, default: void 0 },
925
+ desc: { type: String, default: void 0 },
926
+ compact: Boolean
927
+ },
928
+ setup(props, { slots }) {
929
+ return () => (0, import_vue.h)(
930
+ "div",
931
+ {
932
+ style: {
933
+ display: "grid",
934
+ placeItems: "center",
935
+ textAlign: "center",
936
+ padding: props.compact ? "32px 20px" : "56px 24px"
937
+ }
938
+ },
939
+ [
940
+ (0, import_vue.h)("div", { style: { maxWidth: "340px" } }, [
941
+ (0, import_vue.h)(
942
+ "span",
943
+ {
944
+ style: {
945
+ width: "56px",
946
+ height: "56px",
947
+ borderRadius: "16px",
948
+ display: "grid",
949
+ placeItems: "center",
950
+ margin: "0 auto 18px",
951
+ background: "color-mix(in oklab, var(--accent) 12%, transparent)",
952
+ color: "var(--accent)",
953
+ border: "1px solid color-mix(in oklab, var(--accent) 22%, transparent)"
954
+ }
955
+ },
956
+ slots.icon ? slots.icon() : [svgIcon(INBOX_PATH, 26)]
957
+ ),
958
+ (0, import_vue.h)("div", { style: { fontSize: "17px", fontWeight: 700 } }, props.title),
959
+ props.desc ? (0, import_vue.h)(
960
+ "p",
961
+ {
962
+ style: {
963
+ margin: "7px 0 0",
964
+ color: "var(--text-dim)",
965
+ fontSize: "13.5px",
966
+ lineHeight: 1.6
967
+ }
968
+ },
969
+ props.desc
970
+ ) : null,
971
+ slots.action ? (0, import_vue.h)(
972
+ "div",
973
+ {
974
+ style: {
975
+ marginTop: "20px",
976
+ display: "flex",
977
+ gap: "8px",
978
+ justifyContent: "center"
979
+ }
980
+ },
981
+ slots.action()
982
+ ) : null
983
+ ])
984
+ ]
985
+ );
986
+ }
987
+ });
988
+ var Accordion = (0, import_vue.defineComponent)({
989
+ name: "VspAccordion",
990
+ props: {
991
+ items: { type: Array, default: () => [] },
992
+ multiple: Boolean,
993
+ defaultOpen: { type: Array, default: () => [] }
994
+ },
995
+ setup(props) {
996
+ const open = (0, import_vue.ref)(new Set(props.defaultOpen));
997
+ const toggle = (i) => {
998
+ const s = open.value;
999
+ const n = new Set(props.multiple ? s : []);
1000
+ if (s.has(i)) n.delete(i);
1001
+ else n.add(i);
1002
+ open.value = n;
1003
+ };
1004
+ return () => (0, import_vue.h)(
1005
+ "div",
1006
+ { class: "ui-acc" },
1007
+ props.items.map(
1008
+ (it, i) => (0, import_vue.h)("div", { key: i, class: cx("ui-acc-item", open.value.has(i) && "open") }, [
1009
+ (0, import_vue.h)("button", { type: "button", class: "ui-acc-head", onClick: () => toggle(i) }, [
1010
+ it.title,
1011
+ (0, import_vue.h)(
1012
+ "svg",
1013
+ {
1014
+ class: "chev",
1015
+ viewBox: "0 0 24 24",
1016
+ width: 17,
1017
+ height: 17,
1018
+ fill: "none",
1019
+ stroke: "currentColor",
1020
+ "stroke-width": 2,
1021
+ "stroke-linecap": "round",
1022
+ "stroke-linejoin": "round"
1023
+ },
1024
+ [(0, import_vue.h)("path", { d: ICON_PATHS.chevR })]
1025
+ )
1026
+ ]),
1027
+ (0, import_vue.h)("div", { class: "ui-acc-bodywrap" }, [
1028
+ (0, import_vue.h)("div", null, [(0, import_vue.h)("div", { class: "ui-acc-body" }, it.body)])
1029
+ ])
1030
+ ])
1031
+ )
1032
+ );
1033
+ }
1034
+ });
254
1035
  // Annotate the CommonJS export names for ESM import in node:
255
1036
  0 && (module.exports = {
1037
+ Accordion,
256
1038
  Alert,
1039
+ Avatar,
1040
+ AvatarGroup,
257
1041
  Badge,
1042
+ Banner,
1043
+ Breadcrumb,
258
1044
  Button,
259
1045
  Card,
260
1046
  CardHead,
261
1047
  Checkbox,
1048
+ CircularProgress,
1049
+ DescriptionList,
262
1050
  Divider,
1051
+ EmptyState,
263
1052
  Field,
264
1053
  IconButton,
265
1054
  Input,
266
1055
  Kbd,
1056
+ NativeSelect,
1057
+ Pagination,
1058
+ Progress,
1059
+ Radio,
1060
+ RadioGroup,
1061
+ Segmented,
1062
+ Skeleton,
1063
+ Slider,
267
1064
  Spinner,
1065
+ Stat,
1066
+ Stepper,
268
1067
  Switch,
1068
+ Tabs,
269
1069
  Tag,
270
- Textarea
1070
+ Textarea,
1071
+ Timeline
271
1072
  });
272
1073
  //# sourceMappingURL=index.cjs.map