@vespera-ui/vue 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.
package/dist/index.js CHANGED
@@ -1208,28 +1208,487 @@ var StatCard = defineComponent({
1208
1208
  );
1209
1209
  }
1210
1210
  });
1211
+ var ICON_CHECK = "M20 6L9 17l-5-5";
1212
+ var ICON_DOC = "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8zM14 2v6h6";
1213
+ var ICON_PENCIL = "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4z";
1214
+ var NumberStepper = defineComponent({
1215
+ name: "VspNumberStepper",
1216
+ props: {
1217
+ modelValue: { type: Number, default: 0 },
1218
+ min: { type: Number, default: void 0 },
1219
+ max: { type: Number, default: void 0 },
1220
+ step: { type: Number, default: 1 },
1221
+ unit: { type: String, default: void 0 }
1222
+ },
1223
+ emits: ["update:modelValue"],
1224
+ setup(props, { emit }) {
1225
+ const set = (v) => {
1226
+ let n = v;
1227
+ if (props.min != null && n < props.min) n = props.min;
1228
+ if (props.max != null && n > props.max) n = props.max;
1229
+ emit("update:modelValue", n);
1230
+ };
1231
+ return () => h("div", { class: "ui-stepper" }, [
1232
+ h(
1233
+ "button",
1234
+ {
1235
+ type: "button",
1236
+ "aria-label": "Decrease",
1237
+ disabled: props.min != null && props.modelValue <= props.min,
1238
+ onClick: () => set(props.modelValue - props.step)
1239
+ },
1240
+ "\u2212"
1241
+ ),
1242
+ h("span", { class: "val" }, [
1243
+ props.modelValue,
1244
+ props.unit ? h("i", null, props.unit) : null
1245
+ ]),
1246
+ h(
1247
+ "button",
1248
+ {
1249
+ type: "button",
1250
+ "aria-label": "Increase",
1251
+ disabled: props.max != null && props.modelValue >= props.max,
1252
+ onClick: () => set(props.modelValue + props.step)
1253
+ },
1254
+ "+"
1255
+ )
1256
+ ]);
1257
+ }
1258
+ });
1259
+ var CopyButton = defineComponent({
1260
+ name: "VspCopyButton",
1261
+ props: {
1262
+ text: { type: String, required: true },
1263
+ label: { type: String, default: "Copy" },
1264
+ size: { type: String, default: "sm" }
1265
+ },
1266
+ setup(props) {
1267
+ const done = ref(false);
1268
+ const copy = async () => {
1269
+ try {
1270
+ await navigator.clipboard?.writeText(props.text);
1271
+ } catch {
1272
+ }
1273
+ done.value = true;
1274
+ setTimeout(() => done.value = false, 1400);
1275
+ };
1276
+ return () => h(
1277
+ "button",
1278
+ {
1279
+ type: "button",
1280
+ class: cx("btn", "btn-ghost", props.size === "sm" && "btn-sm"),
1281
+ onClick: copy
1282
+ },
1283
+ [
1284
+ done.value ? h("span", { style: { color: "var(--success)", display: "inline-flex" } }, [
1285
+ svgIcon(ICON_CHECK, 15)
1286
+ ]) : svgIcon(ICON_DOC, 15),
1287
+ done.value ? "Copied" : props.label
1288
+ ]
1289
+ );
1290
+ }
1291
+ });
1292
+ var InlineEdit = defineComponent({
1293
+ name: "VspInlineEdit",
1294
+ props: {
1295
+ value: { type: String, default: "" },
1296
+ placeholder: { type: String, default: "Empty" }
1297
+ },
1298
+ emits: ["save"],
1299
+ setup(props, { emit }) {
1300
+ const editing = ref(false);
1301
+ const draft = ref(props.value);
1302
+ const commit = () => {
1303
+ editing.value = false;
1304
+ if (draft.value !== props.value) emit("save", draft.value);
1305
+ };
1306
+ return () => editing.value ? h("input", {
1307
+ class: "ui-input",
1308
+ value: draft.value,
1309
+ style: { height: "32px", maxWidth: "240px" },
1310
+ onVnodeMounted: (vn) => vn.el?.focus(),
1311
+ onInput: (e) => draft.value = e.target.value,
1312
+ onBlur: commit,
1313
+ onKeydown: (e) => {
1314
+ if (e.key === "Enter") commit();
1315
+ if (e.key === "Escape") {
1316
+ draft.value = props.value;
1317
+ editing.value = false;
1318
+ }
1319
+ }
1320
+ }) : h(
1321
+ "span",
1322
+ {
1323
+ class: "ui-inline",
1324
+ onClick: () => {
1325
+ draft.value = props.value;
1326
+ editing.value = true;
1327
+ }
1328
+ },
1329
+ [
1330
+ h(
1331
+ "span",
1332
+ { style: { color: props.value ? "var(--text)" : "var(--text-faint)" } },
1333
+ props.value || props.placeholder
1334
+ ),
1335
+ h("span", { class: "pen", style: { display: "inline-flex" } }, [
1336
+ svgIcon(ICON_PENCIL, 14)
1337
+ ])
1338
+ ]
1339
+ );
1340
+ }
1341
+ });
1342
+ var svgIconClass = (d, size, cls) => h(
1343
+ "svg",
1344
+ {
1345
+ class: cls,
1346
+ viewBox: "0 0 24 24",
1347
+ width: size,
1348
+ height: size,
1349
+ fill: "none",
1350
+ stroke: "currentColor",
1351
+ "stroke-width": 2,
1352
+ "stroke-linecap": "round",
1353
+ "stroke-linejoin": "round"
1354
+ },
1355
+ [h("path", { d })]
1356
+ );
1357
+ var ICON_LAYERS = "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5";
1358
+ var treeNodeId = (n) => n.id ?? n.label;
1359
+ var VspTreeNode = defineComponent({
1360
+ name: "VspTreeNode",
1361
+ props: {
1362
+ node: { type: Object, required: true },
1363
+ expanded: { type: Object, required: true },
1364
+ selected: { type: String, default: null },
1365
+ toggle: { type: Function, required: true },
1366
+ select: { type: Function, required: true }
1367
+ },
1368
+ setup(props) {
1369
+ return () => {
1370
+ const node = props.node;
1371
+ const id = treeNodeId(node);
1372
+ const kids = node.children ?? [];
1373
+ const hasKids = kids.length > 0;
1374
+ const open = props.expanded.has(id);
1375
+ return h("div", null, [
1376
+ h(
1377
+ "div",
1378
+ {
1379
+ class: cx("ui-tree-row", open && "open", props.selected === id && "sel"),
1380
+ onClick: () => {
1381
+ if (hasKids) props.toggle(id);
1382
+ props.select(id);
1383
+ }
1384
+ },
1385
+ [
1386
+ hasKids ? svgIconClass(ICON_PATHS.chevR, 16, "tw-chev") : h("span", { style: { width: "16px", flexShrink: 0 } }),
1387
+ svgIconClass(hasKids ? ICON_LAYERS : ICON_DOC, 16, "tw-icon"),
1388
+ h(
1389
+ "span",
1390
+ {
1391
+ style: {
1392
+ flex: 1,
1393
+ minWidth: 0,
1394
+ overflow: "hidden",
1395
+ textOverflow: "ellipsis",
1396
+ whiteSpace: "nowrap"
1397
+ }
1398
+ },
1399
+ node.label
1400
+ ),
1401
+ node.badge != null ? h(
1402
+ "span",
1403
+ { class: "mono", style: { fontSize: "11px", color: "var(--text-faint)" } },
1404
+ node.badge
1405
+ ) : null
1406
+ ]
1407
+ ),
1408
+ hasKids && open ? h(
1409
+ "div",
1410
+ { class: "ui-tree-children" },
1411
+ kids.map(
1412
+ (c, i) => h(VspTreeNode, {
1413
+ key: i,
1414
+ node: c,
1415
+ expanded: props.expanded,
1416
+ selected: props.selected,
1417
+ toggle: props.toggle,
1418
+ select: props.select
1419
+ })
1420
+ )
1421
+ ) : null
1422
+ ]);
1423
+ };
1424
+ }
1425
+ });
1426
+ var Tree = defineComponent({
1427
+ name: "VspTree",
1428
+ props: {
1429
+ data: { type: Array, default: () => [] },
1430
+ defaultExpanded: { type: Array, default: () => [] }
1431
+ },
1432
+ setup(props) {
1433
+ const expanded = ref(new Set(props.defaultExpanded));
1434
+ const selected = ref(null);
1435
+ const toggle = (id) => {
1436
+ const n = new Set(expanded.value);
1437
+ if (n.has(id)) n.delete(id);
1438
+ else n.add(id);
1439
+ expanded.value = n;
1440
+ };
1441
+ return () => h(
1442
+ "div",
1443
+ { class: "ui-tree" },
1444
+ props.data.map(
1445
+ (n, i) => h(VspTreeNode, {
1446
+ key: i,
1447
+ node: n,
1448
+ expanded: expanded.value,
1449
+ selected: selected.value,
1450
+ toggle,
1451
+ select: (id) => selected.value = id
1452
+ })
1453
+ )
1454
+ );
1455
+ }
1456
+ });
1457
+ var OTPInput = defineComponent({
1458
+ name: "VspOTPInput",
1459
+ props: {
1460
+ length: { type: Number, default: 6 },
1461
+ modelValue: { type: String, default: "" }
1462
+ },
1463
+ emits: ["update:modelValue"],
1464
+ setup(props, { emit }) {
1465
+ const refs = [];
1466
+ const set = (i, ch) => {
1467
+ const chars = Array.from({ length: props.length }, (_, k) => props.modelValue[k] ?? "");
1468
+ chars[i] = ch.slice(-1);
1469
+ emit("update:modelValue", chars.join(""));
1470
+ if (ch && i < props.length - 1) refs[i + 1]?.focus();
1471
+ };
1472
+ const onKey = (i, e) => {
1473
+ if (e.key === "Backspace" && !props.modelValue[i] && i > 0) refs[i - 1]?.focus();
1474
+ };
1475
+ return () => h(
1476
+ "div",
1477
+ { class: "ui-otp" },
1478
+ Array.from(
1479
+ { length: props.length },
1480
+ (_, i) => h("input", {
1481
+ key: i,
1482
+ ref: (el) => {
1483
+ refs[i] = el;
1484
+ },
1485
+ inputmode: "numeric",
1486
+ maxlength: 1,
1487
+ value: props.modelValue[i] ?? "",
1488
+ onInput: (e) => set(i, e.target.value.replace(/\D/g, "")),
1489
+ onKeydown: (e) => onKey(i, e)
1490
+ })
1491
+ )
1492
+ );
1493
+ }
1494
+ });
1495
+ function niceNum(n) {
1496
+ if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(n % 1e6 === 0 ? 0 : 1) + "M";
1497
+ if (Math.abs(n) >= 1e3) return (n / 1e3).toFixed(n % 1e3 === 0 ? 0 : 1) + "k";
1498
+ return String(n);
1499
+ }
1500
+ var AreaChart = defineComponent({
1501
+ name: "VspAreaChart",
1502
+ props: {
1503
+ series: { type: Array, default: () => [] },
1504
+ labels: { type: Array, default: void 0 },
1505
+ width: { type: Number, default: 760 },
1506
+ height: { type: Number, default: 260 },
1507
+ color: { type: String, default: "var(--accent)" },
1508
+ color2: { type: String, default: "var(--accent-2)" },
1509
+ dual: Boolean
1510
+ },
1511
+ setup(props) {
1512
+ const gid = "ac" + useId().replace(/[^a-zA-Z0-9]/g, "");
1513
+ const txt = (x, y, anchor, val) => h(
1514
+ "text",
1515
+ {
1516
+ x,
1517
+ y,
1518
+ "text-anchor": anchor,
1519
+ "font-size": "10",
1520
+ fill: "var(--text-faint)",
1521
+ "font-family": "var(--font-mono)"
1522
+ },
1523
+ val
1524
+ );
1525
+ return () => {
1526
+ const w = props.width;
1527
+ const height = props.height;
1528
+ const padL = 38;
1529
+ const padB = 26;
1530
+ const padT = 12;
1531
+ const padR = 8;
1532
+ const innerW = Math.max(10, w - padL - padR);
1533
+ const innerH = height - padB - padT;
1534
+ const s0 = props.series[0] ?? [];
1535
+ const s1 = props.series[1];
1536
+ const all = props.dual && s1 ? [...s0, ...s1] : s0;
1537
+ const max = Math.max(...all, 0) * 1.12;
1538
+ const rng = max || 1;
1539
+ const sx = (i, len) => padL + i / Math.max(1, len - 1) * innerW;
1540
+ const sy = (v) => padT + innerH - v / rng * innerH;
1541
+ const mkPts = (arr) => arr.map((v, i) => [sx(i, arr.length), sy(v)]);
1542
+ const lines = (props.dual && s1 ? [s0, s1] : [s0]).map(mkPts);
1543
+ const yTicks = 4;
1544
+ const grid = Array.from({ length: yTicks + 1 }, (_, i) => {
1545
+ const y = sy(max / yTicks * i);
1546
+ return h("g", { key: "g" + i }, [
1547
+ h("line", {
1548
+ x1: padL,
1549
+ x2: w - padR,
1550
+ y1: y,
1551
+ y2: y,
1552
+ stroke: "var(--grid-line)",
1553
+ "stroke-width": "1"
1554
+ }),
1555
+ txt(padL - 8, y + 3.5, "end", niceNum(Math.round(max / yTicks * i)))
1556
+ ]);
1557
+ });
1558
+ const lbls = props.labels ? props.labels.map(
1559
+ (lb, i) => i % Math.ceil(props.labels.length / 7) === 0 ? txt(sx(i, props.labels.length), height - 8, "middle", lb) : null
1560
+ ) : [];
1561
+ const lineEls = lines.map((pts, li) => {
1562
+ const stroke = li === 0 ? props.color : props.color2;
1563
+ const lastPt = pts[pts.length - 1];
1564
+ const firstPt = pts[0];
1565
+ return h("g", { key: "ln" + li }, [
1566
+ li === 0 && firstPt && lastPt ? h("path", {
1567
+ d: `${smoothPath(pts)} L ${lastPt[0]} ${padT + innerH} L ${firstPt[0]} ${padT + innerH} Z`,
1568
+ fill: `url(#${gid})`
1569
+ }) : null,
1570
+ h("path", {
1571
+ d: smoothPath(pts),
1572
+ fill: "none",
1573
+ stroke,
1574
+ "stroke-width": "2.4",
1575
+ "stroke-linecap": "round",
1576
+ "stroke-dasharray": li === 1 ? "5 5" : void 0,
1577
+ style: { opacity: li === 1 ? 0.7 : 1 }
1578
+ })
1579
+ ]);
1580
+ });
1581
+ return h("svg", { width: w, height, style: { display: "block" } }, [
1582
+ h("defs", null, [
1583
+ h("linearGradient", { id: gid, x1: "0", x2: "0", y1: "0", y2: "1" }, [
1584
+ h("stop", { offset: "0", "stop-color": props.color, "stop-opacity": "0.22" }),
1585
+ h("stop", { offset: "1", "stop-color": props.color, "stop-opacity": "0" })
1586
+ ])
1587
+ ]),
1588
+ ...grid,
1589
+ ...lbls,
1590
+ ...lineEls
1591
+ ]);
1592
+ };
1593
+ }
1594
+ });
1595
+ var BarChart = defineComponent({
1596
+ name: "VspBarChart",
1597
+ props: {
1598
+ data: { type: Array, default: () => [] },
1599
+ labels: { type: Array, default: void 0 },
1600
+ width: { type: Number, default: 620 },
1601
+ height: { type: Number, default: 240 },
1602
+ color: { type: String, default: "var(--accent)" }
1603
+ },
1604
+ setup(props) {
1605
+ return () => {
1606
+ const w = props.width;
1607
+ const height = props.height;
1608
+ const padL = 34;
1609
+ const padB = 26;
1610
+ const padT = 10;
1611
+ const innerW = Math.max(10, w - padL - 8);
1612
+ const innerH = height - padB - padT;
1613
+ const max = Math.max(...props.data, 0) * 1.15 || 1;
1614
+ const bw = innerW / (props.data.length || 1);
1615
+ const grid = [0, 0.5, 1].map((f, i) => {
1616
+ const y = padT + innerH - f * innerH;
1617
+ return h("g", { key: "g" + i }, [
1618
+ h("line", { x1: padL, x2: w - 8, y1: y, y2: y, stroke: "var(--grid-line)" }),
1619
+ h(
1620
+ "text",
1621
+ {
1622
+ x: padL - 8,
1623
+ y: y + 3.5,
1624
+ "text-anchor": "end",
1625
+ "font-size": "10",
1626
+ fill: "var(--text-faint)",
1627
+ "font-family": "var(--font-mono)"
1628
+ },
1629
+ niceNum(Math.round(max * f))
1630
+ )
1631
+ ]);
1632
+ });
1633
+ const bars = props.data.map((v, i) => {
1634
+ const bh = v / max * innerH;
1635
+ const x = padL + i * bw + bw * 0.18;
1636
+ const bwi = bw * 0.64;
1637
+ return h("g", { key: "b" + i }, [
1638
+ h("rect", {
1639
+ x,
1640
+ y: padT + innerH - bh,
1641
+ width: bwi,
1642
+ height: bh,
1643
+ rx: "4",
1644
+ fill: `color-mix(in oklab, ${props.color} 78%, transparent)`
1645
+ }),
1646
+ props.labels?.[i] != null ? h(
1647
+ "text",
1648
+ {
1649
+ x: x + bwi / 2,
1650
+ y: height - 8,
1651
+ "text-anchor": "middle",
1652
+ "font-size": "10",
1653
+ fill: "var(--text-faint)",
1654
+ "font-family": "var(--font-mono)"
1655
+ },
1656
+ props.labels[i]
1657
+ ) : null
1658
+ ]);
1659
+ });
1660
+ return h("svg", { width: w, height, style: { display: "block" } }, [...grid, ...bars]);
1661
+ };
1662
+ }
1663
+ });
1211
1664
  export {
1212
1665
  Accordion,
1213
1666
  Alert,
1667
+ AreaChart,
1214
1668
  Avatar,
1215
1669
  AvatarGroup,
1216
1670
  Badge,
1217
1671
  Banner,
1672
+ BarChart,
1218
1673
  Breadcrumb,
1219
1674
  Button,
1220
1675
  Card,
1221
1676
  CardHead,
1222
1677
  Checkbox,
1223
1678
  CircularProgress,
1679
+ CopyButton,
1224
1680
  DescriptionList,
1225
1681
  Divider,
1226
1682
  Donut,
1227
1683
  EmptyState,
1228
1684
  Field,
1229
1685
  IconButton,
1686
+ InlineEdit,
1230
1687
  Input,
1231
1688
  Kbd,
1232
1689
  NativeSelect,
1690
+ NumberStepper,
1691
+ OTPInput,
1233
1692
  Pagination,
1234
1693
  Progress,
1235
1694
  Radio,
@@ -1247,6 +1706,8 @@ export {
1247
1706
  Tag,
1248
1707
  Textarea,
1249
1708
  Timeline,
1709
+ Tree,
1710
+ niceNum,
1250
1711
  smoothPath
1251
1712
  };
1252
1713
  //# sourceMappingURL=index.js.map