barua-ui 0.2.3 → 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/css/utilities.css CHANGED
@@ -470,12 +470,101 @@
470
470
  }
471
471
 
472
472
  /* ---- Symbol effects (SF Symbols bounce / pulse) -------------------------- */
473
+ /*
474
+ * The roster SF Symbols established, because a glyph that answers a press is
475
+ * the cheapest feedback an interface has. Each is one-shot unless it is
476
+ * describing an ongoing state — bounce acknowledges, breathe waits.
477
+ * prefers-reduced-motion is handled once, globally, in base.css.
478
+ */
473
479
  .b-icon--bounce svg, .b-icon--bounce { animation: b-icon-bounce 0.5s var(--b-ease-bounce); }
474
480
  .b-icon--pulse svg, .b-icon--pulse { animation: b-pulse 1.6s var(--b-ease-standard) infinite; }
481
+ .b-icon--scale svg, .b-icon--scale { animation: b-icon-scale 0.32s var(--b-ease-standard); }
482
+ .b-icon--wiggle svg, .b-icon--wiggle { animation: b-icon-wiggle 0.5s var(--b-ease-standard); }
483
+ .b-icon--rotate svg, .b-icon--rotate { animation: b-icon-rotate 1.1s linear infinite; }
484
+ .b-icon--breathe svg, .b-icon--breathe { animation: b-icon-breathe 3s var(--b-ease-standard) infinite; }
485
+ .b-icon--replace svg, .b-icon--replace { animation: b-icon-replace 0.3s var(--b-ease-standard); }
486
+
475
487
  @keyframes b-icon-bounce {
476
488
  30% { translate: 0 -3px; scale: 1.15; }
477
489
  60% { translate: 0 1px; scale: 0.96; }
478
490
  }
491
+ @keyframes b-icon-scale {
492
+ 50% { scale: 1.18; }
493
+ }
494
+ /* Small angles: a glyph that swings far reads as broken rather than lively. */
495
+ @keyframes b-icon-wiggle {
496
+ 20% { rotate: -7deg; }
497
+ 45% { rotate: 6deg; }
498
+ 70% { rotate: -3deg; }
499
+ }
500
+ @keyframes b-icon-rotate {
501
+ to { rotate: 360deg; }
502
+ }
503
+ @keyframes b-icon-breathe {
504
+ 50% { scale: 1.06; opacity: 0.75; }
505
+ }
506
+ /* The swap SwiftUI does between two glyphs: the old one leaves, the new
507
+ arrives. Put the class on the icon at the moment its `name` changes. */
508
+ @keyframes b-icon-replace {
509
+ 0% { scale: 0.6; opacity: 0; }
510
+ 100% { scale: 1; opacity: 1; }
511
+ }
512
+
513
+ /* ---- Scroll-driven reveal ------------------------------------------------
514
+ *
515
+ * The browser drives this from the element's own position in the scrollport,
516
+ * so there is no observer, no scroll listener and no main-thread work: the
517
+ * animation is tied to a timeline rather than to time.
518
+ *
519
+ * Everything stays visible where the timeline is unsupported. An entrance
520
+ * animation that fails closed hides the page. */
521
+ @supports (animation-timeline: view()) {
522
+ .b-reveal {
523
+ animation: b-reveal-in linear both;
524
+ animation-timeline: view();
525
+ /* Finish while the element is still coming up the screen, rather than
526
+ when it is centred — otherwise a reader watches it fade in. */
527
+ animation-range: entry 10% cover 32%;
528
+ }
529
+ .b-reveal--fade { animation-name: b-reveal-fade; }
530
+ .b-reveal--scale { animation-name: b-reveal-scale; }
531
+ .b-reveal--start { animation-name: b-reveal-start; }
532
+ .b-reveal--end { animation-name: b-reveal-end; }
533
+ }
534
+ @keyframes b-reveal-in {
535
+ from { opacity: 0; translate: 0 1.5rem; }
536
+ to { opacity: 1; translate: 0 0; }
537
+ }
538
+ @keyframes b-reveal-fade {
539
+ from { opacity: 0; }
540
+ to { opacity: 1; }
541
+ }
542
+ @keyframes b-reveal-scale {
543
+ from { opacity: 0; scale: 0.94; }
544
+ to { opacity: 1; scale: 1; }
545
+ }
546
+ @keyframes b-reveal-start {
547
+ from { opacity: 0; translate: -1.5rem 0; }
548
+ to { opacity: 1; translate: 0 0; }
549
+ }
550
+ @keyframes b-reveal-end {
551
+ from { opacity: 0; translate: 1.5rem 0; }
552
+ to { opacity: 1; translate: 0 0; }
553
+ }
554
+
555
+ /* ---- Text that shrinks to fit its container -----------------------------
556
+ *
557
+ * SwiftUI's minimumScaleFactor has no CSS equivalent, but a container query
558
+ * unit does the same job honestly: the size is a fraction of the container's
559
+ * width, clamped so it never becomes unreadable or absurd. Needs a container
560
+ * ancestor — .b-fit-text establishes its own. */
561
+ .b-fit-text {
562
+ container-type: inline-size;
563
+ }
564
+ .b-fit-text > * {
565
+ font-size: clamp(var(--b-fit-min, 0.85rem), var(--b-fit-scale, 9cqi), var(--b-fit-max, 3rem));
566
+ white-space: nowrap;
567
+ }
479
568
 
480
569
  /* ---- Safe area (mobile) ------------------------------------------------ */
481
570
  .b-safe-area {
package/dist/index.cjs CHANGED
@@ -97,6 +97,8 @@ __export(index_exports, {
97
97
  CommandList: () => CommandList,
98
98
  CommandPalette: () => CommandPalette,
99
99
  Container: () => Container,
100
+ ContextMenuTarget: () => ContextMenuTarget,
101
+ CountUp: () => CountUp,
100
102
  Dashboard: () => Dashboard,
101
103
  DataGrid: () => DataGrid,
102
104
  DatePicker: () => DatePicker,
@@ -127,6 +129,7 @@ __export(index_exports, {
127
129
  FileTile: () => FileTile,
128
130
  FilterBar: () => FilterBar,
129
131
  FilterChip: () => FilterChip,
132
+ FitText: () => FitText,
130
133
  Footnote: () => Footnote,
131
134
  Footnotes: () => Footnotes,
132
135
  Form: () => Form,
@@ -211,6 +214,7 @@ __export(index_exports, {
211
214
  Resizable: () => Resizable,
212
215
  ResizeHandle: () => ResizeHandle,
213
216
  Result: () => Result,
217
+ Reveal: () => Reveal,
214
218
  SafeArea: () => SafeArea,
215
219
  ScatterChart: () => ScatterChart,
216
220
  ScrollArea: () => ScrollArea,
@@ -2466,6 +2470,90 @@ var CommandCenterTile = block("div", "b-cc__tile", "CommandCenterTile");
2466
2470
  var CommandCenterLabel = block("div", "b-cc__label", "CommandCenterLabel");
2467
2471
  var CommandCenterStatus = block("div", "b-cc__status", "CommandCenterStatus");
2468
2472
  var ImageLens = block("div", "b-lens", "ImageLens");
2473
+
2474
+ // src/motion.tsx
2475
+ var import_react21 = require("react");
2476
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2477
+ var Reveal = (0, import_react21.forwardRef)(function Reveal2({ as: Tag2 = "div", variant = "up", className, children, ...rest }, ref) {
2478
+ const Component = Tag2;
2479
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2480
+ Component,
2481
+ {
2482
+ ref,
2483
+ className: cn("b-reveal", variant !== "up" && `b-reveal--${variant}`, className),
2484
+ ...rest,
2485
+ children
2486
+ }
2487
+ );
2488
+ });
2489
+ var FitText = (0, import_react21.forwardRef)(function FitText2({ min, max, scale, className, style, children, ...rest }, ref) {
2490
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2491
+ "div",
2492
+ {
2493
+ ref,
2494
+ className: cn("b-fit-text", className),
2495
+ style: {
2496
+ ...min ? { ["--b-fit-min"]: min } : null,
2497
+ ...max ? { ["--b-fit-max"]: max } : null,
2498
+ ...scale ? { ["--b-fit-scale"]: scale } : null,
2499
+ ...style
2500
+ },
2501
+ ...rest,
2502
+ children
2503
+ }
2504
+ );
2505
+ });
2506
+ var CountUp = (0, import_react21.forwardRef)(function CountUp2({ to, decimals = 0, duration = 700, prefix = "", suffix = "", grouped = true, className, ...rest }, ref) {
2507
+ const node = (0, import_react21.useRef)(null);
2508
+ const from = (0, import_react21.useRef)(0);
2509
+ (0, import_react21.useEffect)(() => {
2510
+ const el = node.current;
2511
+ if (!el) return;
2512
+ const start = from.current;
2513
+ const target = to;
2514
+ from.current = to;
2515
+ const render = (value) => {
2516
+ const text = grouped ? value.toLocaleString(void 0, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) : value.toFixed(decimals);
2517
+ el.textContent = `${prefix}${text}${suffix}`;
2518
+ };
2519
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
2520
+ render(target);
2521
+ return;
2522
+ }
2523
+ let frame = 0;
2524
+ const began = performance.now();
2525
+ const step = (now) => {
2526
+ const p = Math.min(1, (now - began) / duration);
2527
+ render(start + (target - start) * (1 - Math.pow(1 - p, 3)));
2528
+ if (p < 1) frame = requestAnimationFrame(step);
2529
+ };
2530
+ frame = requestAnimationFrame(step);
2531
+ return () => cancelAnimationFrame(frame);
2532
+ }, [to, decimals, duration, prefix, suffix, grouped]);
2533
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2534
+ "span",
2535
+ {
2536
+ ref: (el) => {
2537
+ node.current = el;
2538
+ if (typeof ref === "function") ref(el);
2539
+ else if (ref) ref.current = el;
2540
+ },
2541
+ className: cn("b-tabular-nums", className),
2542
+ ...rest,
2543
+ children: [
2544
+ prefix,
2545
+ 0 .toFixed(decimals),
2546
+ suffix
2547
+ ]
2548
+ }
2549
+ );
2550
+ });
2551
+ var ContextMenuTarget = (0, import_react21.forwardRef)(
2552
+ function ContextMenuTarget2({ as: Tag2 = "div", menu, children, ...rest }, ref) {
2553
+ const Component = Tag2;
2554
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Component, { ref, "data-b-contextmenu": menu, ...rest, children });
2555
+ }
2556
+ );
2469
2557
  // Annotate the CommonJS export names for ESM import in node:
2470
2558
  0 && (module.exports = {
2471
2559
  Accordion,
@@ -2544,6 +2632,8 @@ var ImageLens = block("div", "b-lens", "ImageLens");
2544
2632
  CommandList,
2545
2633
  CommandPalette,
2546
2634
  Container,
2635
+ ContextMenuTarget,
2636
+ CountUp,
2547
2637
  Dashboard,
2548
2638
  DataGrid,
2549
2639
  DatePicker,
@@ -2574,6 +2664,7 @@ var ImageLens = block("div", "b-lens", "ImageLens");
2574
2664
  FileTile,
2575
2665
  FilterBar,
2576
2666
  FilterChip,
2667
+ FitText,
2577
2668
  Footnote,
2578
2669
  Footnotes,
2579
2670
  Form,
@@ -2658,6 +2749,7 @@ var ImageLens = block("div", "b-lens", "ImageLens");
2658
2749
  Resizable,
2659
2750
  ResizeHandle,
2660
2751
  Result,
2752
+ Reveal,
2661
2753
  SafeArea,
2662
2754
  ScatterChart,
2663
2755
  ScrollArea,