@fluix-ui/vanilla 0.0.5 → 0.0.7
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/README.md +106 -2
- package/dist/index.cjs +701 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -3
- package/dist/index.d.ts +69 -3
- package/dist/index.global.js +1473 -0
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +701 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.global.js
CHANGED
|
@@ -458,6 +458,776 @@ var Fluix = (function (exports) {
|
|
|
458
458
|
getViewportAttrs,
|
|
459
459
|
connect: connectToast
|
|
460
460
|
};
|
|
461
|
+
var NOTCH_DEFAULTS = {
|
|
462
|
+
roundness: 20,
|
|
463
|
+
pillHeight: 40,
|
|
464
|
+
pillMinWidth: 40
|
|
465
|
+
};
|
|
466
|
+
function createNotchMachine(initialConfig) {
|
|
467
|
+
const store = createStore({
|
|
468
|
+
open: false,
|
|
469
|
+
config: { ...initialConfig },
|
|
470
|
+
contentSize: { w: 0, h: 0 },
|
|
471
|
+
baseSize: { w: NOTCH_DEFAULTS.pillMinWidth, h: NOTCH_DEFAULTS.pillHeight }
|
|
472
|
+
});
|
|
473
|
+
function open() {
|
|
474
|
+
store.update((prev) => prev.open ? prev : { ...prev, open: true });
|
|
475
|
+
}
|
|
476
|
+
function close() {
|
|
477
|
+
store.update((prev) => prev.open ? { ...prev, open: false } : prev);
|
|
478
|
+
}
|
|
479
|
+
function toggle() {
|
|
480
|
+
store.update((prev) => ({ ...prev, open: !prev.open }));
|
|
481
|
+
}
|
|
482
|
+
function setContentSize(size) {
|
|
483
|
+
store.update((prev) => {
|
|
484
|
+
if (prev.contentSize.w === size.w && prev.contentSize.h === size.h) return prev;
|
|
485
|
+
return { ...prev, contentSize: size };
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function setBaseSize(size) {
|
|
489
|
+
store.update((prev) => {
|
|
490
|
+
if (prev.baseSize.w === size.w && prev.baseSize.h === size.h) return prev;
|
|
491
|
+
return { ...prev, baseSize: size };
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
function configure(config) {
|
|
495
|
+
store.update((prev) => ({ ...prev, config: { ...prev.config, ...config } }));
|
|
496
|
+
}
|
|
497
|
+
function destroy() {
|
|
498
|
+
}
|
|
499
|
+
return { store, open, close, toggle, setContentSize, setBaseSize, configure, destroy };
|
|
500
|
+
}
|
|
501
|
+
function getNotchAttrs(context) {
|
|
502
|
+
const root = {
|
|
503
|
+
"data-fluix-notch": "",
|
|
504
|
+
"data-open": String(context.open),
|
|
505
|
+
"data-position": context.position
|
|
506
|
+
};
|
|
507
|
+
if (context.theme) {
|
|
508
|
+
root["data-theme"] = context.theme;
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
root,
|
|
512
|
+
canvas: {
|
|
513
|
+
"data-fluix-notch-canvas": ""
|
|
514
|
+
},
|
|
515
|
+
pill: {
|
|
516
|
+
"data-fluix-notch-pill": ""
|
|
517
|
+
},
|
|
518
|
+
content: {
|
|
519
|
+
"data-fluix-notch-content": "",
|
|
520
|
+
"data-open": String(context.open)
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
var MENU_DEFAULTS = {
|
|
525
|
+
orientation: "vertical",
|
|
526
|
+
roundness: 16
|
|
527
|
+
};
|
|
528
|
+
function createMenuMachine(initialConfig) {
|
|
529
|
+
const store = createStore({
|
|
530
|
+
activeId: initialConfig?.initialActiveId ?? null,
|
|
531
|
+
config: { ...initialConfig }
|
|
532
|
+
});
|
|
533
|
+
function setActive(id) {
|
|
534
|
+
store.update((prev) => {
|
|
535
|
+
if (prev.activeId === id) return prev;
|
|
536
|
+
return { ...prev, activeId: id };
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
function configure(config) {
|
|
540
|
+
store.update((prev) => ({ ...prev, config: { ...prev.config, ...config } }));
|
|
541
|
+
}
|
|
542
|
+
function destroy() {
|
|
543
|
+
}
|
|
544
|
+
return { store, setActive, configure, destroy };
|
|
545
|
+
}
|
|
546
|
+
function getMenuAttrs(context) {
|
|
547
|
+
const root = {
|
|
548
|
+
"data-fluix-menu": "",
|
|
549
|
+
"data-orientation": context.orientation
|
|
550
|
+
};
|
|
551
|
+
if (context.theme) {
|
|
552
|
+
root["data-theme"] = context.theme;
|
|
553
|
+
}
|
|
554
|
+
if (context.variant) {
|
|
555
|
+
root["data-variant"] = context.variant;
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
root,
|
|
559
|
+
list: {
|
|
560
|
+
"data-fluix-menu-list": ""
|
|
561
|
+
},
|
|
562
|
+
canvas: {
|
|
563
|
+
"data-fluix-menu-canvas": ""
|
|
564
|
+
},
|
|
565
|
+
indicator: {
|
|
566
|
+
"data-fluix-menu-indicator": ""
|
|
567
|
+
},
|
|
568
|
+
item(itemContext) {
|
|
569
|
+
const item = {
|
|
570
|
+
"data-fluix-menu-item": "",
|
|
571
|
+
"data-menu-id": itemContext.id,
|
|
572
|
+
"data-state": itemContext.active ? "active" : "inactive"
|
|
573
|
+
};
|
|
574
|
+
if (itemContext.disabled) {
|
|
575
|
+
item["data-disabled"] = "true";
|
|
576
|
+
}
|
|
577
|
+
return item;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
var ITEM_SELECTOR = "[data-fluix-menu-item]";
|
|
582
|
+
var TAB_CURVE_RADIUS = 14;
|
|
583
|
+
function readItemFrame(root, activeId, padding, variant, orientation) {
|
|
584
|
+
const activeItem = root.querySelector(
|
|
585
|
+
`${ITEM_SELECTOR}[data-menu-id="${CSS.escape(activeId)}"]`
|
|
586
|
+
);
|
|
587
|
+
if (!activeItem) return null;
|
|
588
|
+
const rootRect = root.getBoundingClientRect();
|
|
589
|
+
const itemRect = activeItem.getBoundingClientRect();
|
|
590
|
+
const width = Math.max(0, itemRect.width + padding * 2);
|
|
591
|
+
const height = Math.max(0, itemRect.height + padding * 2);
|
|
592
|
+
const x = itemRect.left - rootRect.left - padding;
|
|
593
|
+
const y = itemRect.top - rootRect.top - padding;
|
|
594
|
+
if (variant === "tab") {
|
|
595
|
+
if (orientation === "horizontal") {
|
|
596
|
+
const extendedHeight = rootRect.height - y + 1;
|
|
597
|
+
return {
|
|
598
|
+
x,
|
|
599
|
+
y,
|
|
600
|
+
width,
|
|
601
|
+
height: extendedHeight,
|
|
602
|
+
radius: height / 2,
|
|
603
|
+
// use original item height for top pill arc
|
|
604
|
+
visible: width > 0 && height > 0
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
const extendedWidth = rootRect.width - x;
|
|
608
|
+
return {
|
|
609
|
+
x,
|
|
610
|
+
y,
|
|
611
|
+
width: extendedWidth,
|
|
612
|
+
height,
|
|
613
|
+
radius: height / 2,
|
|
614
|
+
visible: width > 0 && height > 0
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
x,
|
|
619
|
+
y,
|
|
620
|
+
width,
|
|
621
|
+
height,
|
|
622
|
+
radius: height / 2,
|
|
623
|
+
visible: width > 0 && height > 0
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function generateTabPath(frame, cr) {
|
|
627
|
+
const { x, y, width, height } = frame;
|
|
628
|
+
const r = Math.min(frame.radius, height / 2, width / 2);
|
|
629
|
+
const rw = x + width;
|
|
630
|
+
const concaveR = Math.min(cr, height / 2, Math.max(0, width / 2 - r));
|
|
631
|
+
return [
|
|
632
|
+
`M ${x + r} ${y}`,
|
|
633
|
+
`L ${rw - concaveR} ${y}`,
|
|
634
|
+
`Q ${rw} ${y} ${rw} ${y - concaveR}`,
|
|
635
|
+
`L ${rw} ${y + height + concaveR}`,
|
|
636
|
+
`Q ${rw} ${y + height} ${rw - concaveR} ${y + height}`,
|
|
637
|
+
`L ${x + r} ${y + height}`,
|
|
638
|
+
`A ${r} ${r} 0 0 1 ${x} ${y + height - r}`,
|
|
639
|
+
`L ${x} ${y + r}`,
|
|
640
|
+
`A ${r} ${r} 0 0 1 ${x + r} ${y}`,
|
|
641
|
+
"Z"
|
|
642
|
+
].join(" ");
|
|
643
|
+
}
|
|
644
|
+
function generateHorizontalTabPath(frame, cr) {
|
|
645
|
+
const { x, y, width, height } = frame;
|
|
646
|
+
const r = Math.min(frame.radius, width / 2, height / 2);
|
|
647
|
+
const bottom = y + height;
|
|
648
|
+
const concaveR = Math.min(cr, width / 2, Math.max(0, height - r));
|
|
649
|
+
return [
|
|
650
|
+
// Start at top-left, after the rounded corner
|
|
651
|
+
`M ${x} ${y + r}`,
|
|
652
|
+
// Arc from left edge to top edge (top-left rounded corner)
|
|
653
|
+
`A ${r} ${r} 0 0 1 ${x + r} ${y}`,
|
|
654
|
+
// Top edge to top-right corner
|
|
655
|
+
`L ${x + width - r} ${y}`,
|
|
656
|
+
// Arc from top edge to right edge (top-right rounded corner)
|
|
657
|
+
`A ${r} ${r} 0 0 1 ${x + width} ${y + r}`,
|
|
658
|
+
// Right edge down to bottom-right concave
|
|
659
|
+
`L ${x + width} ${bottom - concaveR}`,
|
|
660
|
+
// Concave curve at bottom-right (curves outward)
|
|
661
|
+
`Q ${x + width} ${bottom} ${x + width + concaveR} ${bottom}`,
|
|
662
|
+
// Flat bottom edge (off to the right, then back to the left)
|
|
663
|
+
`L ${x - concaveR} ${bottom}`,
|
|
664
|
+
// Concave curve at bottom-left (curves outward)
|
|
665
|
+
`Q ${x} ${bottom} ${x} ${bottom - concaveR}`,
|
|
666
|
+
// Left edge back up
|
|
667
|
+
`L ${x} ${y + r}`,
|
|
668
|
+
"Z"
|
|
669
|
+
].join(" ");
|
|
670
|
+
}
|
|
671
|
+
function applyFrame(indicator, frame, variant, orientation) {
|
|
672
|
+
if (variant === "tab") {
|
|
673
|
+
const path = indicator;
|
|
674
|
+
const generator = orientation === "horizontal" ? generateHorizontalTabPath : generateTabPath;
|
|
675
|
+
path.setAttribute("d", generator(frame, TAB_CURVE_RADIUS));
|
|
676
|
+
path.setAttribute("opacity", frame.visible ? "1" : "0");
|
|
677
|
+
} else {
|
|
678
|
+
const rect = indicator;
|
|
679
|
+
rect.setAttribute("x", String(frame.x));
|
|
680
|
+
rect.setAttribute("y", String(frame.y));
|
|
681
|
+
rect.setAttribute("width", String(frame.width));
|
|
682
|
+
rect.setAttribute("height", String(frame.height));
|
|
683
|
+
rect.setAttribute("rx", String(frame.radius));
|
|
684
|
+
rect.setAttribute("ry", String(frame.radius));
|
|
685
|
+
rect.setAttribute("opacity", frame.visible ? "1" : "0");
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
function frameEquals(a, b) {
|
|
689
|
+
if (!a || !b) return false;
|
|
690
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height && a.radius === b.radius && a.visible === b.visible;
|
|
691
|
+
}
|
|
692
|
+
function simulateSpringValues(config) {
|
|
693
|
+
const { stiffness, damping, mass } = config;
|
|
694
|
+
const dt = 1 / 120;
|
|
695
|
+
const maxDuration = 3;
|
|
696
|
+
const samples = [0];
|
|
697
|
+
let position = 0;
|
|
698
|
+
let velocity = 0;
|
|
699
|
+
let t = 0;
|
|
700
|
+
while (t < maxDuration) {
|
|
701
|
+
const acceleration = (-stiffness * (position - 1) - damping * velocity) / mass;
|
|
702
|
+
const midVelocity = velocity + acceleration * (dt / 2);
|
|
703
|
+
const midPosition = position + velocity * (dt / 2);
|
|
704
|
+
const midAcceleration = (-stiffness * (midPosition - 1) - damping * midVelocity) / mass;
|
|
705
|
+
velocity = velocity + midAcceleration * dt;
|
|
706
|
+
position = position + midVelocity * dt;
|
|
707
|
+
t += dt;
|
|
708
|
+
samples.push(position);
|
|
709
|
+
if (Math.abs(position - 1) < 1e-3 && Math.abs(velocity) < 1e-3) break;
|
|
710
|
+
}
|
|
711
|
+
samples.push(1);
|
|
712
|
+
return samples;
|
|
713
|
+
}
|
|
714
|
+
function lerp(a, b, t) {
|
|
715
|
+
return a + (b - a) * t;
|
|
716
|
+
}
|
|
717
|
+
function easeOutCubic(t) {
|
|
718
|
+
return 1 - (1 - t) ** 3;
|
|
719
|
+
}
|
|
720
|
+
var EXIT_MS = 130;
|
|
721
|
+
function animateTabEnter(path, from, to, cr, spring) {
|
|
722
|
+
const rightEdge = from.x + from.width;
|
|
723
|
+
const samples = simulateSpringValues(spring);
|
|
724
|
+
const count = samples.length;
|
|
725
|
+
const durationMs = count / 120 * 1e3;
|
|
726
|
+
const startTime = performance.now();
|
|
727
|
+
let cancelled = false;
|
|
728
|
+
const handle = {
|
|
729
|
+
onfinish: null,
|
|
730
|
+
cancel() {
|
|
731
|
+
cancelled = true;
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
function tick() {
|
|
735
|
+
if (cancelled) return;
|
|
736
|
+
const elapsed = performance.now() - startTime;
|
|
737
|
+
const progress = Math.min(elapsed / durationMs, 1);
|
|
738
|
+
const idx = Math.min(Math.floor(progress * (count - 1)), count - 1);
|
|
739
|
+
const t = samples[idx];
|
|
740
|
+
const frame = {
|
|
741
|
+
x: lerp(rightEdge, to.x, t),
|
|
742
|
+
y: to.y,
|
|
743
|
+
width: lerp(0, to.width, t),
|
|
744
|
+
height: to.height,
|
|
745
|
+
radius: to.radius
|
|
746
|
+
};
|
|
747
|
+
path.setAttribute("d", generateTabPath(frame, cr));
|
|
748
|
+
if (progress < 1) {
|
|
749
|
+
requestAnimationFrame(tick);
|
|
750
|
+
} else {
|
|
751
|
+
handle.onfinish?.();
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
requestAnimationFrame(tick);
|
|
755
|
+
return handle;
|
|
756
|
+
}
|
|
757
|
+
function animateHorizontalTabEnter(path, from, to, cr, spring) {
|
|
758
|
+
const bottomEdge = from.y + from.height;
|
|
759
|
+
const samples = simulateSpringValues(spring);
|
|
760
|
+
const count = samples.length;
|
|
761
|
+
const durationMs = count / 120 * 1e3;
|
|
762
|
+
const startTime = performance.now();
|
|
763
|
+
let cancelled = false;
|
|
764
|
+
const handle = {
|
|
765
|
+
onfinish: null,
|
|
766
|
+
cancel() {
|
|
767
|
+
cancelled = true;
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
function tick() {
|
|
771
|
+
if (cancelled) return;
|
|
772
|
+
const elapsed = performance.now() - startTime;
|
|
773
|
+
const progress = Math.min(elapsed / durationMs, 1);
|
|
774
|
+
const idx = Math.min(Math.floor(progress * (count - 1)), count - 1);
|
|
775
|
+
const t = samples[idx];
|
|
776
|
+
const frame = {
|
|
777
|
+
x: to.x,
|
|
778
|
+
y: lerp(bottomEdge, to.y, t),
|
|
779
|
+
width: to.width,
|
|
780
|
+
height: lerp(0, to.height, t),
|
|
781
|
+
radius: to.radius
|
|
782
|
+
};
|
|
783
|
+
path.setAttribute("d", generateHorizontalTabPath(frame, cr));
|
|
784
|
+
if (progress < 1) {
|
|
785
|
+
requestAnimationFrame(tick);
|
|
786
|
+
} else {
|
|
787
|
+
handle.onfinish?.();
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
requestAnimationFrame(tick);
|
|
791
|
+
return handle;
|
|
792
|
+
}
|
|
793
|
+
function animateTabIndicator(path, from, to, cr, spring, onEnterStart) {
|
|
794
|
+
const rightEdge = from.x + from.width;
|
|
795
|
+
const enterSamples = simulateSpringValues({
|
|
796
|
+
stiffness: spring.stiffness * 3,
|
|
797
|
+
damping: spring.damping * 1.8,
|
|
798
|
+
mass: spring.mass
|
|
799
|
+
});
|
|
800
|
+
const enterCount = enterSamples.length;
|
|
801
|
+
const enterMs = enterCount / 120 * 1e3;
|
|
802
|
+
const totalMs = EXIT_MS + enterMs;
|
|
803
|
+
const startTime = performance.now();
|
|
804
|
+
let cancelled = false;
|
|
805
|
+
let enteredPhase2 = false;
|
|
806
|
+
const handle = {
|
|
807
|
+
onfinish: null,
|
|
808
|
+
cancel() {
|
|
809
|
+
cancelled = true;
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
function tick() {
|
|
813
|
+
if (cancelled) return;
|
|
814
|
+
const elapsed = performance.now() - startTime;
|
|
815
|
+
let frame;
|
|
816
|
+
if (elapsed < EXIT_MS) {
|
|
817
|
+
const t = easeOutCubic(elapsed / EXIT_MS);
|
|
818
|
+
frame = {
|
|
819
|
+
x: lerp(from.x, rightEdge, t),
|
|
820
|
+
y: from.y,
|
|
821
|
+
width: lerp(from.width, 0, t),
|
|
822
|
+
height: from.height,
|
|
823
|
+
radius: from.radius,
|
|
824
|
+
visible: true
|
|
825
|
+
};
|
|
826
|
+
} else {
|
|
827
|
+
if (!enteredPhase2) {
|
|
828
|
+
enteredPhase2 = true;
|
|
829
|
+
onEnterStart?.();
|
|
830
|
+
}
|
|
831
|
+
const phaseElapsed = elapsed - EXIT_MS;
|
|
832
|
+
const phaseProgress = Math.min(phaseElapsed / enterMs, 1);
|
|
833
|
+
const idx = Math.min(
|
|
834
|
+
Math.floor(phaseProgress * (enterCount - 1)),
|
|
835
|
+
enterCount - 1
|
|
836
|
+
);
|
|
837
|
+
const t = enterSamples[idx];
|
|
838
|
+
frame = {
|
|
839
|
+
x: lerp(rightEdge, to.x, t),
|
|
840
|
+
y: to.y,
|
|
841
|
+
width: lerp(0, to.width, t),
|
|
842
|
+
height: to.height,
|
|
843
|
+
radius: to.radius,
|
|
844
|
+
visible: true
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
path.setAttribute("d", generateTabPath(frame, cr));
|
|
848
|
+
if (elapsed < totalMs) {
|
|
849
|
+
requestAnimationFrame(tick);
|
|
850
|
+
} else {
|
|
851
|
+
handle.onfinish?.();
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
requestAnimationFrame(tick);
|
|
855
|
+
return handle;
|
|
856
|
+
}
|
|
857
|
+
function animateHorizontalTabIndicator(path, from, to, cr, spring, onEnterStart) {
|
|
858
|
+
const bottomEdge = from.y + from.height;
|
|
859
|
+
const enterSamples = simulateSpringValues({
|
|
860
|
+
stiffness: spring.stiffness * 3,
|
|
861
|
+
damping: spring.damping * 1.8,
|
|
862
|
+
mass: spring.mass
|
|
863
|
+
});
|
|
864
|
+
const enterCount = enterSamples.length;
|
|
865
|
+
const enterMs = enterCount / 120 * 1e3;
|
|
866
|
+
const totalMs = EXIT_MS + enterMs;
|
|
867
|
+
const startTime = performance.now();
|
|
868
|
+
let cancelled = false;
|
|
869
|
+
let enteredPhase2 = false;
|
|
870
|
+
const handle = {
|
|
871
|
+
onfinish: null,
|
|
872
|
+
cancel() {
|
|
873
|
+
cancelled = true;
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
function tick() {
|
|
877
|
+
if (cancelled) return;
|
|
878
|
+
const elapsed = performance.now() - startTime;
|
|
879
|
+
let frame;
|
|
880
|
+
if (elapsed < EXIT_MS) {
|
|
881
|
+
const t = easeOutCubic(elapsed / EXIT_MS);
|
|
882
|
+
frame = {
|
|
883
|
+
x: from.x,
|
|
884
|
+
y: lerp(from.y, bottomEdge, t),
|
|
885
|
+
width: from.width,
|
|
886
|
+
height: lerp(from.height, 0, t),
|
|
887
|
+
radius: from.radius,
|
|
888
|
+
visible: true
|
|
889
|
+
};
|
|
890
|
+
} else {
|
|
891
|
+
if (!enteredPhase2) {
|
|
892
|
+
enteredPhase2 = true;
|
|
893
|
+
onEnterStart?.();
|
|
894
|
+
}
|
|
895
|
+
const phaseElapsed = elapsed - EXIT_MS;
|
|
896
|
+
const phaseProgress = Math.min(phaseElapsed / enterMs, 1);
|
|
897
|
+
const idx = Math.min(
|
|
898
|
+
Math.floor(phaseProgress * (enterCount - 1)),
|
|
899
|
+
enterCount - 1
|
|
900
|
+
);
|
|
901
|
+
const t = enterSamples[idx];
|
|
902
|
+
frame = {
|
|
903
|
+
x: to.x,
|
|
904
|
+
y: lerp(bottomEdge, to.y, t),
|
|
905
|
+
width: to.width,
|
|
906
|
+
height: lerp(0, to.height, t),
|
|
907
|
+
radius: to.radius,
|
|
908
|
+
visible: true
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
path.setAttribute("d", generateHorizontalTabPath(frame, cr));
|
|
912
|
+
if (elapsed < totalMs) {
|
|
913
|
+
requestAnimationFrame(tick);
|
|
914
|
+
} else {
|
|
915
|
+
handle.onfinish?.();
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
requestAnimationFrame(tick);
|
|
919
|
+
return handle;
|
|
920
|
+
}
|
|
921
|
+
var STRETCH_MS = 150;
|
|
922
|
+
function animatePillMorph(rect, from, to, spring) {
|
|
923
|
+
const stretchedX = Math.min(from.x, to.x);
|
|
924
|
+
const stretchedRight = Math.max(from.x + from.width, to.x + to.width);
|
|
925
|
+
const stretchedWidth = stretchedRight - stretchedX;
|
|
926
|
+
const contractSamples = simulateSpringValues({
|
|
927
|
+
stiffness: spring.stiffness * 2.5,
|
|
928
|
+
damping: spring.damping * 1.6,
|
|
929
|
+
mass: spring.mass
|
|
930
|
+
});
|
|
931
|
+
const contractCount = contractSamples.length;
|
|
932
|
+
const contractMs = contractCount / 120 * 1e3;
|
|
933
|
+
const totalMs = STRETCH_MS + contractMs;
|
|
934
|
+
const startTime = performance.now();
|
|
935
|
+
let cancelled = false;
|
|
936
|
+
const handle = {
|
|
937
|
+
onfinish: null,
|
|
938
|
+
cancel() {
|
|
939
|
+
cancelled = true;
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
function applyRect(x, y, w, h, r) {
|
|
943
|
+
rect.setAttribute("x", String(x));
|
|
944
|
+
rect.setAttribute("y", String(y));
|
|
945
|
+
rect.setAttribute("width", String(w));
|
|
946
|
+
rect.setAttribute("height", String(h));
|
|
947
|
+
rect.setAttribute("rx", String(r));
|
|
948
|
+
rect.setAttribute("ry", String(r));
|
|
949
|
+
}
|
|
950
|
+
function tick() {
|
|
951
|
+
if (cancelled) return;
|
|
952
|
+
const elapsed = performance.now() - startTime;
|
|
953
|
+
if (elapsed < STRETCH_MS) {
|
|
954
|
+
const t = easeOutCubic(elapsed / STRETCH_MS);
|
|
955
|
+
applyRect(
|
|
956
|
+
lerp(from.x, stretchedX, t),
|
|
957
|
+
lerp(from.y, to.y, t),
|
|
958
|
+
lerp(from.width, stretchedWidth, t),
|
|
959
|
+
lerp(from.height, to.height, t),
|
|
960
|
+
lerp(from.radius, to.radius, t)
|
|
961
|
+
);
|
|
962
|
+
} else {
|
|
963
|
+
const phaseElapsed = elapsed - STRETCH_MS;
|
|
964
|
+
const phaseProgress = Math.min(phaseElapsed / contractMs, 1);
|
|
965
|
+
const idx = Math.min(Math.floor(phaseProgress * (contractCount - 1)), contractCount - 1);
|
|
966
|
+
const t = contractSamples[idx];
|
|
967
|
+
applyRect(
|
|
968
|
+
lerp(stretchedX, to.x, t),
|
|
969
|
+
to.y,
|
|
970
|
+
lerp(stretchedWidth, to.width, t),
|
|
971
|
+
to.height,
|
|
972
|
+
to.radius
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
if (elapsed < totalMs) {
|
|
976
|
+
requestAnimationFrame(tick);
|
|
977
|
+
} else {
|
|
978
|
+
handle.onfinish?.();
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
requestAnimationFrame(tick);
|
|
982
|
+
return handle;
|
|
983
|
+
}
|
|
984
|
+
function connectMenu(options) {
|
|
985
|
+
const spring = options.spring ?? FLUIX_SPRING;
|
|
986
|
+
const padding = options.padding ?? 6;
|
|
987
|
+
const variant = options.variant;
|
|
988
|
+
const orientation = options.orientation;
|
|
989
|
+
const cleanups = [];
|
|
990
|
+
let currentAnimation = null;
|
|
991
|
+
let lastFrame = null;
|
|
992
|
+
let rafId = 0;
|
|
993
|
+
let resizeObserver = null;
|
|
994
|
+
let mutationObserver = null;
|
|
995
|
+
let previousActiveId = null;
|
|
996
|
+
let animOldId = null;
|
|
997
|
+
let animNewId = null;
|
|
998
|
+
let animPhase = null;
|
|
999
|
+
function setItemState(id, state) {
|
|
1000
|
+
const el = options.root.querySelector(
|
|
1001
|
+
`${ITEM_SELECTOR}[data-menu-id="${CSS.escape(id)}"]`
|
|
1002
|
+
);
|
|
1003
|
+
if (el && el.dataset["state"] !== state) {
|
|
1004
|
+
el.dataset["state"] = state;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function enforceAnimStates() {
|
|
1008
|
+
if (!animPhase) return;
|
|
1009
|
+
if (animPhase === "exit") {
|
|
1010
|
+
if (animOldId) setItemState(animOldId, "active");
|
|
1011
|
+
if (animNewId) setItemState(animNewId, "inactive");
|
|
1012
|
+
} else {
|
|
1013
|
+
if (animOldId) setItemState(animOldId, "inactive");
|
|
1014
|
+
if (animNewId) setItemState(animNewId, "active");
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
function clearAnimState() {
|
|
1018
|
+
animOldId = null;
|
|
1019
|
+
animNewId = null;
|
|
1020
|
+
animPhase = null;
|
|
1021
|
+
}
|
|
1022
|
+
function apply(frame) {
|
|
1023
|
+
applyFrame(options.indicator, frame, variant, orientation);
|
|
1024
|
+
}
|
|
1025
|
+
const updateIndicator = (immediate = false) => {
|
|
1026
|
+
const activeId = options.getActiveId();
|
|
1027
|
+
const nextFrame = activeId ? readItemFrame(options.root, activeId, padding, variant, orientation) : null;
|
|
1028
|
+
const fallbackFrame = nextFrame ?? lastFrame ?? {
|
|
1029
|
+
x: 0,
|
|
1030
|
+
y: 0,
|
|
1031
|
+
width: 0,
|
|
1032
|
+
height: 0,
|
|
1033
|
+
radius: 0,
|
|
1034
|
+
visible: false
|
|
1035
|
+
};
|
|
1036
|
+
if (!lastFrame) {
|
|
1037
|
+
lastFrame = fallbackFrame;
|
|
1038
|
+
previousActiveId = activeId;
|
|
1039
|
+
apply(fallbackFrame);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (frameEquals(lastFrame, fallbackFrame)) return;
|
|
1043
|
+
if (currentAnimation) {
|
|
1044
|
+
currentAnimation.cancel();
|
|
1045
|
+
currentAnimation = null;
|
|
1046
|
+
clearAnimState();
|
|
1047
|
+
}
|
|
1048
|
+
if (variant === "tab" && !immediate && fallbackFrame.visible && !lastFrame.visible) {
|
|
1049
|
+
const to2 = fallbackFrame;
|
|
1050
|
+
lastFrame = to2;
|
|
1051
|
+
previousActiveId = activeId;
|
|
1052
|
+
const isHorizontal = orientation === "horizontal";
|
|
1053
|
+
const collapsedFrom = isHorizontal ? {
|
|
1054
|
+
x: to2.x,
|
|
1055
|
+
y: to2.y + to2.height,
|
|
1056
|
+
// bottom edge
|
|
1057
|
+
width: to2.width,
|
|
1058
|
+
height: 0,
|
|
1059
|
+
radius: to2.radius
|
|
1060
|
+
} : {
|
|
1061
|
+
x: to2.x + to2.width,
|
|
1062
|
+
// right edge
|
|
1063
|
+
y: to2.y,
|
|
1064
|
+
width: 0,
|
|
1065
|
+
height: to2.height,
|
|
1066
|
+
radius: to2.radius
|
|
1067
|
+
};
|
|
1068
|
+
options.indicator.setAttribute("opacity", "1");
|
|
1069
|
+
const springConfig = { stiffness: spring.stiffness ?? 170, damping: spring.damping ?? 18, mass: spring.mass ?? 1 };
|
|
1070
|
+
const enterAnim = isHorizontal ? animateHorizontalTabEnter(
|
|
1071
|
+
options.indicator,
|
|
1072
|
+
collapsedFrom,
|
|
1073
|
+
to2,
|
|
1074
|
+
TAB_CURVE_RADIUS,
|
|
1075
|
+
springConfig
|
|
1076
|
+
) : animateTabEnter(
|
|
1077
|
+
options.indicator,
|
|
1078
|
+
collapsedFrom,
|
|
1079
|
+
to2,
|
|
1080
|
+
TAB_CURVE_RADIUS,
|
|
1081
|
+
springConfig
|
|
1082
|
+
);
|
|
1083
|
+
currentAnimation = enterAnim;
|
|
1084
|
+
enterAnim.onfinish = () => {
|
|
1085
|
+
currentAnimation = null;
|
|
1086
|
+
const settledId = options.getActiveId();
|
|
1087
|
+
const settled = settledId ? readItemFrame(options.root, settledId, padding, variant, orientation) : null;
|
|
1088
|
+
if (settled) {
|
|
1089
|
+
lastFrame = settled;
|
|
1090
|
+
apply(settled);
|
|
1091
|
+
} else {
|
|
1092
|
+
apply(to2);
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (immediate || !fallbackFrame.visible || !lastFrame.visible) {
|
|
1098
|
+
lastFrame = fallbackFrame;
|
|
1099
|
+
previousActiveId = activeId;
|
|
1100
|
+
apply(fallbackFrame);
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const from = lastFrame;
|
|
1104
|
+
const to = fallbackFrame;
|
|
1105
|
+
const oldActiveId = previousActiveId;
|
|
1106
|
+
const newActiveId = activeId;
|
|
1107
|
+
lastFrame = to;
|
|
1108
|
+
previousActiveId = activeId;
|
|
1109
|
+
let animation = null;
|
|
1110
|
+
if (variant === "tab") {
|
|
1111
|
+
animOldId = oldActiveId;
|
|
1112
|
+
animNewId = newActiveId;
|
|
1113
|
+
animPhase = "exit";
|
|
1114
|
+
enforceAnimStates();
|
|
1115
|
+
const springConfig = { stiffness: spring.stiffness ?? 170, damping: spring.damping ?? 18, mass: spring.mass ?? 1 };
|
|
1116
|
+
const onEnterStart = () => {
|
|
1117
|
+
animPhase = "enter";
|
|
1118
|
+
enforceAnimStates();
|
|
1119
|
+
};
|
|
1120
|
+
animation = orientation === "horizontal" ? animateHorizontalTabIndicator(
|
|
1121
|
+
options.indicator,
|
|
1122
|
+
from,
|
|
1123
|
+
to,
|
|
1124
|
+
TAB_CURVE_RADIUS,
|
|
1125
|
+
springConfig,
|
|
1126
|
+
onEnterStart
|
|
1127
|
+
) : animateTabIndicator(
|
|
1128
|
+
options.indicator,
|
|
1129
|
+
from,
|
|
1130
|
+
to,
|
|
1131
|
+
TAB_CURVE_RADIUS,
|
|
1132
|
+
springConfig,
|
|
1133
|
+
onEnterStart
|
|
1134
|
+
);
|
|
1135
|
+
} else {
|
|
1136
|
+
animation = animatePillMorph(
|
|
1137
|
+
options.indicator,
|
|
1138
|
+
from,
|
|
1139
|
+
to,
|
|
1140
|
+
{ stiffness: spring.stiffness ?? 170, damping: spring.damping ?? 18, mass: spring.mass ?? 1 }
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
if (!animation) {
|
|
1144
|
+
apply(to);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
currentAnimation = animation;
|
|
1148
|
+
animation.onfinish = () => {
|
|
1149
|
+
currentAnimation = null;
|
|
1150
|
+
if (variant === "tab") {
|
|
1151
|
+
if (newActiveId) setItemState(newActiveId, "active");
|
|
1152
|
+
if (oldActiveId && oldActiveId !== newActiveId) setItemState(oldActiveId, "inactive");
|
|
1153
|
+
clearAnimState();
|
|
1154
|
+
}
|
|
1155
|
+
const settledId = options.getActiveId();
|
|
1156
|
+
const settled = settledId ? readItemFrame(options.root, settledId, padding, variant, orientation) : null;
|
|
1157
|
+
if (settled) {
|
|
1158
|
+
lastFrame = settled;
|
|
1159
|
+
apply(settled);
|
|
1160
|
+
} else {
|
|
1161
|
+
apply(to);
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
};
|
|
1165
|
+
const sync = (immediate = false) => {
|
|
1166
|
+
cancelAnimationFrame(rafId);
|
|
1167
|
+
rafId = requestAnimationFrame(() => updateIndicator(immediate));
|
|
1168
|
+
};
|
|
1169
|
+
const handleClick = (event) => {
|
|
1170
|
+
if (!options.onSelect) return;
|
|
1171
|
+
const target = event.target;
|
|
1172
|
+
if (!target) return;
|
|
1173
|
+
const item = target.closest(ITEM_SELECTOR);
|
|
1174
|
+
if (!item || item.dataset["disabled"] === "true") return;
|
|
1175
|
+
const id = item.dataset["menuId"];
|
|
1176
|
+
if (!id) return;
|
|
1177
|
+
options.onSelect(id);
|
|
1178
|
+
};
|
|
1179
|
+
options.root.addEventListener("click", handleClick);
|
|
1180
|
+
cleanups.push(() => options.root.removeEventListener("click", handleClick));
|
|
1181
|
+
const scheduleSync = () => {
|
|
1182
|
+
if (animPhase) return;
|
|
1183
|
+
sync(false);
|
|
1184
|
+
};
|
|
1185
|
+
resizeObserver = new ResizeObserver(scheduleSync);
|
|
1186
|
+
resizeObserver.observe(options.root);
|
|
1187
|
+
for (const item of options.root.querySelectorAll(ITEM_SELECTOR)) {
|
|
1188
|
+
resizeObserver.observe(item);
|
|
1189
|
+
}
|
|
1190
|
+
cleanups.push(() => {
|
|
1191
|
+
resizeObserver?.disconnect();
|
|
1192
|
+
resizeObserver = null;
|
|
1193
|
+
});
|
|
1194
|
+
mutationObserver = new MutationObserver(() => {
|
|
1195
|
+
if (animPhase) {
|
|
1196
|
+
enforceAnimStates();
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (!resizeObserver) return;
|
|
1200
|
+
resizeObserver.disconnect();
|
|
1201
|
+
resizeObserver.observe(options.root);
|
|
1202
|
+
for (const item of options.root.querySelectorAll(ITEM_SELECTOR)) {
|
|
1203
|
+
resizeObserver.observe(item);
|
|
1204
|
+
}
|
|
1205
|
+
sync(false);
|
|
1206
|
+
});
|
|
1207
|
+
mutationObserver.observe(options.root, {
|
|
1208
|
+
childList: true,
|
|
1209
|
+
subtree: true,
|
|
1210
|
+
attributes: true,
|
|
1211
|
+
attributeFilter: ["data-menu-id", "data-state"]
|
|
1212
|
+
});
|
|
1213
|
+
cleanups.push(() => {
|
|
1214
|
+
mutationObserver?.disconnect();
|
|
1215
|
+
mutationObserver = null;
|
|
1216
|
+
});
|
|
1217
|
+
sync(true);
|
|
1218
|
+
return {
|
|
1219
|
+
sync,
|
|
1220
|
+
destroy() {
|
|
1221
|
+
cancelAnimationFrame(rafId);
|
|
1222
|
+
if (currentAnimation) {
|
|
1223
|
+
currentAnimation.cancel();
|
|
1224
|
+
currentAnimation = null;
|
|
1225
|
+
}
|
|
1226
|
+
for (const cleanup of cleanups) cleanup();
|
|
1227
|
+
cleanups.length = 0;
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
461
1231
|
|
|
462
1232
|
// src/toast.ts
|
|
463
1233
|
var WIDTH = 350;
|
|
@@ -1240,6 +2010,709 @@ var Fluix = (function (exports) {
|
|
|
1240
2010
|
};
|
|
1241
2011
|
}
|
|
1242
2012
|
|
|
2013
|
+
// src/notch.ts
|
|
2014
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
2015
|
+
function applyAttrs2(el, attrs) {
|
|
2016
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
2017
|
+
el.setAttribute(key, value);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
function resolveContent(source) {
|
|
2021
|
+
if (source instanceof HTMLElement) return source;
|
|
2022
|
+
const span = document.createElement("span");
|
|
2023
|
+
span.textContent = source;
|
|
2024
|
+
return span;
|
|
2025
|
+
}
|
|
2026
|
+
function createNotch(container, options) {
|
|
2027
|
+
let {
|
|
2028
|
+
trigger = "click",
|
|
2029
|
+
position = "top-center",
|
|
2030
|
+
spring,
|
|
2031
|
+
dotSize = 36,
|
|
2032
|
+
roundness = NOTCH_DEFAULTS.roundness,
|
|
2033
|
+
theme = "dark",
|
|
2034
|
+
fill,
|
|
2035
|
+
open: controlledOpen,
|
|
2036
|
+
onOpenChange
|
|
2037
|
+
} = options;
|
|
2038
|
+
const springConfig = () => spring ?? FLUIX_SPRING;
|
|
2039
|
+
const machine2 = createNotchMachine({
|
|
2040
|
+
position,
|
|
2041
|
+
trigger,
|
|
2042
|
+
roundness,
|
|
2043
|
+
fill,
|
|
2044
|
+
spring
|
|
2045
|
+
});
|
|
2046
|
+
let snapshot = machine2.store.getSnapshot();
|
|
2047
|
+
let prevOpenVal;
|
|
2048
|
+
const blur = () => Math.min(10, Math.max(6, roundness * 0.45));
|
|
2049
|
+
const collapsedW = () => dotSize;
|
|
2050
|
+
const collapsedH = () => dotSize;
|
|
2051
|
+
let contentSize = { w: 200, h: 44 };
|
|
2052
|
+
const hlPad = 12;
|
|
2053
|
+
const expandedW = () => contentSize.w + hlPad * 2;
|
|
2054
|
+
const expandedH = () => Math.max(contentSize.h + hlPad, dotSize);
|
|
2055
|
+
const targetW = () => snapshot.open ? expandedW() : collapsedW();
|
|
2056
|
+
const targetH = () => snapshot.open ? expandedH() : collapsedH();
|
|
2057
|
+
const rootW = () => Math.max(expandedW(), collapsedW());
|
|
2058
|
+
const rootH = () => Math.max(expandedH(), collapsedH());
|
|
2059
|
+
const prev = { w: 0, h: 0, initialized: false };
|
|
2060
|
+
let currentAnim = null;
|
|
2061
|
+
let highlightAnim = null;
|
|
2062
|
+
const hlPrev = { x: 0, y: 0, w: 0, h: 0, visible: false };
|
|
2063
|
+
const measureEl = document.createElement("div");
|
|
2064
|
+
measureEl.setAttribute("data-fluix-notch-measure", "");
|
|
2065
|
+
measureEl.appendChild(resolveContent(options.content).cloneNode(true));
|
|
2066
|
+
container.appendChild(measureEl);
|
|
2067
|
+
const rootEl = document.createElement("div");
|
|
2068
|
+
const attrs = getNotchAttrs({ open: snapshot.open, position, theme });
|
|
2069
|
+
applyAttrs2(rootEl, attrs.root);
|
|
2070
|
+
rootEl.style.width = `${rootW()}px`;
|
|
2071
|
+
rootEl.style.height = `${rootH()}px`;
|
|
2072
|
+
const canvasDiv = document.createElement("div");
|
|
2073
|
+
applyAttrs2(canvasDiv, attrs.canvas);
|
|
2074
|
+
const svg = document.createElementNS(SVG_NS2, "svg");
|
|
2075
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2076
|
+
svg.setAttribute("width", String(rootW()));
|
|
2077
|
+
svg.setAttribute("height", String(rootH()));
|
|
2078
|
+
svg.setAttribute("viewBox", `0 0 ${rootW()} ${rootH()}`);
|
|
2079
|
+
svg.setAttribute("aria-hidden", "true");
|
|
2080
|
+
const defs = document.createElementNS(SVG_NS2, "defs");
|
|
2081
|
+
const filter = document.createElementNS(SVG_NS2, "filter");
|
|
2082
|
+
filter.setAttribute("id", "fluix-notch-goo");
|
|
2083
|
+
filter.setAttribute("x", "-20%");
|
|
2084
|
+
filter.setAttribute("y", "-20%");
|
|
2085
|
+
filter.setAttribute("width", "140%");
|
|
2086
|
+
filter.setAttribute("height", "140%");
|
|
2087
|
+
filter.setAttribute("color-interpolation-filters", "sRGB");
|
|
2088
|
+
const feBlur = document.createElementNS(SVG_NS2, "feGaussianBlur");
|
|
2089
|
+
feBlur.setAttribute("in", "SourceGraphic");
|
|
2090
|
+
feBlur.setAttribute("stdDeviation", String(blur()));
|
|
2091
|
+
feBlur.setAttribute("result", "blur");
|
|
2092
|
+
const feCM = document.createElementNS(SVG_NS2, "feColorMatrix");
|
|
2093
|
+
feCM.setAttribute("in", "blur");
|
|
2094
|
+
feCM.setAttribute("type", "matrix");
|
|
2095
|
+
feCM.setAttribute("values", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10");
|
|
2096
|
+
feCM.setAttribute("result", "goo");
|
|
2097
|
+
const feComp = document.createElementNS(SVG_NS2, "feComposite");
|
|
2098
|
+
feComp.setAttribute("in", "SourceGraphic");
|
|
2099
|
+
feComp.setAttribute("in2", "goo");
|
|
2100
|
+
feComp.setAttribute("operator", "atop");
|
|
2101
|
+
filter.appendChild(feBlur);
|
|
2102
|
+
filter.appendChild(feCM);
|
|
2103
|
+
filter.appendChild(feComp);
|
|
2104
|
+
defs.appendChild(filter);
|
|
2105
|
+
svg.appendChild(defs);
|
|
2106
|
+
const gGroup = document.createElementNS(SVG_NS2, "g");
|
|
2107
|
+
gGroup.setAttribute("filter", "url(#fluix-notch-goo)");
|
|
2108
|
+
const svgRectEl = document.createElementNS(SVG_NS2, "rect");
|
|
2109
|
+
const cw = collapsedW();
|
|
2110
|
+
const ch = collapsedH();
|
|
2111
|
+
svgRectEl.setAttribute("x", String((rootW() - cw) / 2));
|
|
2112
|
+
svgRectEl.setAttribute("y", String((rootH() - ch) / 2));
|
|
2113
|
+
svgRectEl.setAttribute("width", String(cw));
|
|
2114
|
+
svgRectEl.setAttribute("height", String(ch));
|
|
2115
|
+
svgRectEl.setAttribute("rx", String(cw / 2));
|
|
2116
|
+
svgRectEl.setAttribute("ry", String(ch / 2));
|
|
2117
|
+
svgRectEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
|
|
2118
|
+
gGroup.appendChild(svgRectEl);
|
|
2119
|
+
svg.appendChild(gGroup);
|
|
2120
|
+
const hoverBlobEl = document.createElementNS(SVG_NS2, "rect");
|
|
2121
|
+
hoverBlobEl.setAttribute("x", String((rootW() - cw) / 2));
|
|
2122
|
+
hoverBlobEl.setAttribute("y", String((rootH() - ch) / 2));
|
|
2123
|
+
hoverBlobEl.setAttribute("width", "0");
|
|
2124
|
+
hoverBlobEl.setAttribute("height", "0");
|
|
2125
|
+
hoverBlobEl.setAttribute("rx", "0");
|
|
2126
|
+
hoverBlobEl.setAttribute("ry", "0");
|
|
2127
|
+
hoverBlobEl.setAttribute("opacity", "0");
|
|
2128
|
+
hoverBlobEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
|
|
2129
|
+
gGroup.appendChild(hoverBlobEl);
|
|
2130
|
+
canvasDiv.appendChild(svg);
|
|
2131
|
+
rootEl.appendChild(canvasDiv);
|
|
2132
|
+
const pillDiv = document.createElement("div");
|
|
2133
|
+
applyAttrs2(pillDiv, attrs.pill);
|
|
2134
|
+
pillDiv.style.width = `${dotSize}px`;
|
|
2135
|
+
pillDiv.style.height = `${dotSize}px`;
|
|
2136
|
+
pillDiv.appendChild(resolveContent(options.pill));
|
|
2137
|
+
rootEl.appendChild(pillDiv);
|
|
2138
|
+
const contentDiv = document.createElement("div");
|
|
2139
|
+
applyAttrs2(contentDiv, attrs.content);
|
|
2140
|
+
contentDiv.appendChild(resolveContent(options.content));
|
|
2141
|
+
rootEl.appendChild(contentDiv);
|
|
2142
|
+
container.appendChild(rootEl);
|
|
2143
|
+
prev.w = cw;
|
|
2144
|
+
prev.h = ch;
|
|
2145
|
+
prev.initialized = true;
|
|
2146
|
+
let measureRaf = 0;
|
|
2147
|
+
const measureObs = new ResizeObserver(() => {
|
|
2148
|
+
cancelAnimationFrame(measureRaf);
|
|
2149
|
+
measureRaf = requestAnimationFrame(() => {
|
|
2150
|
+
const r = measureEl.getBoundingClientRect();
|
|
2151
|
+
if (r.width > 0 && r.height > 0) {
|
|
2152
|
+
const newSize = { w: Math.ceil(r.width), h: Math.ceil(r.height) };
|
|
2153
|
+
if (newSize.w !== contentSize.w || newSize.h !== contentSize.h) {
|
|
2154
|
+
contentSize = newSize;
|
|
2155
|
+
updateLayout();
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
});
|
|
2160
|
+
measureObs.observe(measureEl);
|
|
2161
|
+
function onItemEnter(e) {
|
|
2162
|
+
const target = e.target.closest("a, button");
|
|
2163
|
+
if (!target || !snapshot.open) return;
|
|
2164
|
+
const rootRect = rootEl.getBoundingClientRect();
|
|
2165
|
+
const itemW = target.offsetWidth;
|
|
2166
|
+
const itemH = target.offsetHeight;
|
|
2167
|
+
const itemRect = target.getBoundingClientRect();
|
|
2168
|
+
const itemCenterX = itemRect.left + itemRect.width / 2;
|
|
2169
|
+
const itemCenterY = itemRect.top + itemRect.height / 2;
|
|
2170
|
+
const padX = 8;
|
|
2171
|
+
const padY = 4;
|
|
2172
|
+
const blobOvershoot = Math.max(6, roundness * 0.35);
|
|
2173
|
+
const toW = itemW + padX * 2;
|
|
2174
|
+
const toH = Math.max(itemH + padY * 2, rootRect.height + blobOvershoot * 2);
|
|
2175
|
+
const toX = itemCenterX - rootRect.left - toW / 2;
|
|
2176
|
+
const toY = itemCenterY - rootRect.top - toH / 2;
|
|
2177
|
+
const toRx = toH / 2;
|
|
2178
|
+
const fromX = hlPrev.visible ? hlPrev.x : toX + toW / 2;
|
|
2179
|
+
const fromY = hlPrev.visible ? hlPrev.y : toY + toH / 2;
|
|
2180
|
+
const fromW = hlPrev.visible ? hlPrev.w : 0;
|
|
2181
|
+
const fromH = hlPrev.visible ? hlPrev.h : 0;
|
|
2182
|
+
const fromR = hlPrev.visible ? hlPrev.h / 2 : 0;
|
|
2183
|
+
if (highlightAnim) {
|
|
2184
|
+
highlightAnim.cancel();
|
|
2185
|
+
highlightAnim = null;
|
|
2186
|
+
}
|
|
2187
|
+
const sc = springConfig();
|
|
2188
|
+
const a = animateSpring(
|
|
2189
|
+
hoverBlobEl,
|
|
2190
|
+
{
|
|
2191
|
+
x: { from: fromX, to: toX, unit: "px" },
|
|
2192
|
+
y: { from: fromY, to: toY, unit: "px" },
|
|
2193
|
+
width: { from: fromW, to: toW, unit: "px" },
|
|
2194
|
+
height: { from: fromH, to: toH, unit: "px" },
|
|
2195
|
+
rx: { from: fromR, to: toRx, unit: "px" },
|
|
2196
|
+
ry: { from: fromR, to: toRx, unit: "px" }
|
|
2197
|
+
},
|
|
2198
|
+
{ ...sc, stiffness: (sc.stiffness ?? 300) * 1.2 }
|
|
2199
|
+
);
|
|
2200
|
+
hlPrev.x = toX;
|
|
2201
|
+
hlPrev.y = toY;
|
|
2202
|
+
hlPrev.w = toW;
|
|
2203
|
+
hlPrev.h = toH;
|
|
2204
|
+
if (a) {
|
|
2205
|
+
highlightAnim = a;
|
|
2206
|
+
a.onfinish = () => {
|
|
2207
|
+
highlightAnim = null;
|
|
2208
|
+
hoverBlobEl.setAttribute("x", String(toX));
|
|
2209
|
+
hoverBlobEl.setAttribute("y", String(toY));
|
|
2210
|
+
hoverBlobEl.setAttribute("width", String(toW));
|
|
2211
|
+
hoverBlobEl.setAttribute("height", String(toH));
|
|
2212
|
+
hoverBlobEl.setAttribute("rx", String(toRx));
|
|
2213
|
+
hoverBlobEl.setAttribute("ry", String(toRx));
|
|
2214
|
+
hoverBlobEl.setAttribute("opacity", "1");
|
|
2215
|
+
};
|
|
2216
|
+
} else {
|
|
2217
|
+
hoverBlobEl.setAttribute("x", String(toX));
|
|
2218
|
+
hoverBlobEl.setAttribute("y", String(toY));
|
|
2219
|
+
hoverBlobEl.setAttribute("width", String(toW));
|
|
2220
|
+
hoverBlobEl.setAttribute("height", String(toH));
|
|
2221
|
+
hoverBlobEl.setAttribute("rx", String(toRx));
|
|
2222
|
+
hoverBlobEl.setAttribute("ry", String(toRx));
|
|
2223
|
+
hoverBlobEl.setAttribute("opacity", "1");
|
|
2224
|
+
}
|
|
2225
|
+
hoverBlobEl.setAttribute("opacity", "1");
|
|
2226
|
+
hlPrev.visible = true;
|
|
2227
|
+
}
|
|
2228
|
+
function resetHoverBlobImmediate() {
|
|
2229
|
+
if (highlightAnim) {
|
|
2230
|
+
highlightAnim.cancel();
|
|
2231
|
+
highlightAnim = null;
|
|
2232
|
+
}
|
|
2233
|
+
hoverBlobEl.setAttribute("x", String(rootW() / 2));
|
|
2234
|
+
hoverBlobEl.setAttribute("y", String(rootH() / 2));
|
|
2235
|
+
hoverBlobEl.setAttribute("width", "0");
|
|
2236
|
+
hoverBlobEl.setAttribute("height", "0");
|
|
2237
|
+
hoverBlobEl.setAttribute("rx", "0");
|
|
2238
|
+
hoverBlobEl.setAttribute("ry", "0");
|
|
2239
|
+
hoverBlobEl.setAttribute("opacity", "0");
|
|
2240
|
+
hlPrev.visible = false;
|
|
2241
|
+
}
|
|
2242
|
+
function onItemLeave() {
|
|
2243
|
+
if (!hlPrev.visible) return;
|
|
2244
|
+
const cx = hlPrev.x + hlPrev.w / 2;
|
|
2245
|
+
const cy = hlPrev.y + hlPrev.h / 2;
|
|
2246
|
+
const sc = springConfig();
|
|
2247
|
+
const a = animateSpring(
|
|
2248
|
+
hoverBlobEl,
|
|
2249
|
+
{
|
|
2250
|
+
x: { from: hlPrev.x, to: cx, unit: "px" },
|
|
2251
|
+
y: { from: hlPrev.y, to: cy, unit: "px" },
|
|
2252
|
+
width: { from: hlPrev.w, to: 0, unit: "px" },
|
|
2253
|
+
height: { from: hlPrev.h, to: 0, unit: "px" },
|
|
2254
|
+
rx: { from: hlPrev.h / 2, to: 0, unit: "px" },
|
|
2255
|
+
ry: { from: hlPrev.h / 2, to: 0, unit: "px" }
|
|
2256
|
+
},
|
|
2257
|
+
{ ...sc, stiffness: (sc.stiffness ?? 300) * 1.2 }
|
|
2258
|
+
);
|
|
2259
|
+
if (a) {
|
|
2260
|
+
highlightAnim = a;
|
|
2261
|
+
a.onfinish = () => {
|
|
2262
|
+
highlightAnim = null;
|
|
2263
|
+
hoverBlobEl.setAttribute("x", String(cx));
|
|
2264
|
+
hoverBlobEl.setAttribute("y", String(cy));
|
|
2265
|
+
hoverBlobEl.setAttribute("width", "0");
|
|
2266
|
+
hoverBlobEl.setAttribute("height", "0");
|
|
2267
|
+
hoverBlobEl.setAttribute("rx", "0");
|
|
2268
|
+
hoverBlobEl.setAttribute("ry", "0");
|
|
2269
|
+
hoverBlobEl.setAttribute("opacity", "0");
|
|
2270
|
+
};
|
|
2271
|
+
} else {
|
|
2272
|
+
hoverBlobEl.setAttribute("x", String(cx));
|
|
2273
|
+
hoverBlobEl.setAttribute("y", String(cy));
|
|
2274
|
+
hoverBlobEl.setAttribute("width", "0");
|
|
2275
|
+
hoverBlobEl.setAttribute("height", "0");
|
|
2276
|
+
hoverBlobEl.setAttribute("rx", "0");
|
|
2277
|
+
hoverBlobEl.setAttribute("ry", "0");
|
|
2278
|
+
hoverBlobEl.setAttribute("opacity", "0");
|
|
2279
|
+
}
|
|
2280
|
+
hlPrev.visible = false;
|
|
2281
|
+
}
|
|
2282
|
+
function handleOpen() {
|
|
2283
|
+
if (controlledOpen === void 0) machine2.open();
|
|
2284
|
+
else onOpenChange?.(true);
|
|
2285
|
+
}
|
|
2286
|
+
function handleClose() {
|
|
2287
|
+
if (controlledOpen === void 0) machine2.close();
|
|
2288
|
+
else onOpenChange?.(false);
|
|
2289
|
+
}
|
|
2290
|
+
function handleToggle() {
|
|
2291
|
+
if (controlledOpen === void 0) machine2.toggle();
|
|
2292
|
+
else onOpenChange?.(!snapshot.open);
|
|
2293
|
+
}
|
|
2294
|
+
function onMouseEnter() {
|
|
2295
|
+
if (trigger === "hover") handleOpen();
|
|
2296
|
+
}
|
|
2297
|
+
function onMouseLeave() {
|
|
2298
|
+
if (trigger === "hover") {
|
|
2299
|
+
handleClose();
|
|
2300
|
+
resetHoverBlobImmediate();
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
onItemLeave();
|
|
2304
|
+
}
|
|
2305
|
+
function onClick() {
|
|
2306
|
+
if (trigger === "click") handleToggle();
|
|
2307
|
+
}
|
|
2308
|
+
rootEl.addEventListener("mouseenter", onMouseEnter);
|
|
2309
|
+
rootEl.addEventListener("mouseleave", onMouseLeave);
|
|
2310
|
+
rootEl.addEventListener("mouseover", onItemEnter);
|
|
2311
|
+
rootEl.addEventListener("click", onClick);
|
|
2312
|
+
function animateRect() {
|
|
2313
|
+
const tw = targetW();
|
|
2314
|
+
const th = targetH();
|
|
2315
|
+
if (tw === prev.w && th === prev.h) return;
|
|
2316
|
+
if (currentAnim) {
|
|
2317
|
+
currentAnim.cancel();
|
|
2318
|
+
currentAnim = null;
|
|
2319
|
+
}
|
|
2320
|
+
const rw = rootW();
|
|
2321
|
+
const rh = rootH();
|
|
2322
|
+
const fromW = prev.w;
|
|
2323
|
+
const fromH = prev.h;
|
|
2324
|
+
const fromX = (rw - fromW) / 2;
|
|
2325
|
+
const fromY = (rh - fromH) / 2;
|
|
2326
|
+
const toX = (rw - tw) / 2;
|
|
2327
|
+
const toY = (rh - th) / 2;
|
|
2328
|
+
prev.w = tw;
|
|
2329
|
+
prev.h = th;
|
|
2330
|
+
const isCollapsing = tw === collapsedW() && th === collapsedH();
|
|
2331
|
+
const wasCollapsed = fromW === collapsedW() && fromH === collapsedH();
|
|
2332
|
+
const fromRx = wasCollapsed ? collapsedW() / 2 : roundness;
|
|
2333
|
+
const toRx = isCollapsing ? collapsedW() / 2 : roundness;
|
|
2334
|
+
const a = animateSpring(
|
|
2335
|
+
svgRectEl,
|
|
2336
|
+
{
|
|
2337
|
+
width: { from: fromW, to: tw, unit: "px" },
|
|
2338
|
+
height: { from: fromH, to: th, unit: "px" },
|
|
2339
|
+
x: { from: fromX, to: toX, unit: "px" },
|
|
2340
|
+
y: { from: fromY, to: toY, unit: "px" },
|
|
2341
|
+
rx: { from: fromRx, to: toRx, unit: "px" },
|
|
2342
|
+
ry: { from: fromRx, to: toRx, unit: "px" }
|
|
2343
|
+
},
|
|
2344
|
+
springConfig()
|
|
2345
|
+
);
|
|
2346
|
+
if (a) {
|
|
2347
|
+
currentAnim = a;
|
|
2348
|
+
a.onfinish = () => {
|
|
2349
|
+
currentAnim = null;
|
|
2350
|
+
svgRectEl.setAttribute("width", String(tw));
|
|
2351
|
+
svgRectEl.setAttribute("height", String(th));
|
|
2352
|
+
svgRectEl.setAttribute("x", String(toX));
|
|
2353
|
+
svgRectEl.setAttribute("y", String(toY));
|
|
2354
|
+
svgRectEl.setAttribute("rx", String(toRx));
|
|
2355
|
+
svgRectEl.setAttribute("ry", String(toRx));
|
|
2356
|
+
};
|
|
2357
|
+
} else {
|
|
2358
|
+
svgRectEl.setAttribute("width", String(tw));
|
|
2359
|
+
svgRectEl.setAttribute("height", String(th));
|
|
2360
|
+
svgRectEl.setAttribute("x", String(toX));
|
|
2361
|
+
svgRectEl.setAttribute("y", String(toY));
|
|
2362
|
+
svgRectEl.setAttribute("rx", String(toRx));
|
|
2363
|
+
svgRectEl.setAttribute("ry", String(toRx));
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
function updateLayout() {
|
|
2367
|
+
const isOpen = snapshot.open;
|
|
2368
|
+
const newAttrs = getNotchAttrs({ open: isOpen, position, theme });
|
|
2369
|
+
applyAttrs2(rootEl, newAttrs.root);
|
|
2370
|
+
rootEl.style.width = `${rootW()}px`;
|
|
2371
|
+
rootEl.style.height = `${rootH()}px`;
|
|
2372
|
+
svg.setAttribute("width", String(rootW()));
|
|
2373
|
+
svg.setAttribute("height", String(rootH()));
|
|
2374
|
+
svg.setAttribute("viewBox", `0 0 ${rootW()} ${rootH()}`);
|
|
2375
|
+
feBlur.setAttribute("stdDeviation", String(blur()));
|
|
2376
|
+
svgRectEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
|
|
2377
|
+
hoverBlobEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
|
|
2378
|
+
applyAttrs2(contentDiv, newAttrs.content);
|
|
2379
|
+
animateRect();
|
|
2380
|
+
if (!isOpen) {
|
|
2381
|
+
resetHoverBlobImmediate();
|
|
2382
|
+
}
|
|
2383
|
+
document.documentElement.style.setProperty(
|
|
2384
|
+
"--fluix-notch-offset",
|
|
2385
|
+
`${rootH()}px`
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
const unsubscribe = machine2.store.subscribe(() => {
|
|
2389
|
+
const next = machine2.store.getSnapshot();
|
|
2390
|
+
snapshot = next;
|
|
2391
|
+
if (controlledOpen !== void 0) {
|
|
2392
|
+
if (controlledOpen && !next.open) machine2.open();
|
|
2393
|
+
else if (!controlledOpen && next.open) machine2.close();
|
|
2394
|
+
}
|
|
2395
|
+
if (prevOpenVal !== void 0 && prevOpenVal !== next.open) {
|
|
2396
|
+
onOpenChange?.(next.open);
|
|
2397
|
+
}
|
|
2398
|
+
prevOpenVal = next.open;
|
|
2399
|
+
updateLayout();
|
|
2400
|
+
});
|
|
2401
|
+
updateLayout();
|
|
2402
|
+
document.documentElement.style.setProperty(
|
|
2403
|
+
"--fluix-notch-offset",
|
|
2404
|
+
`${rootH()}px`
|
|
2405
|
+
);
|
|
2406
|
+
return {
|
|
2407
|
+
open() {
|
|
2408
|
+
handleOpen();
|
|
2409
|
+
},
|
|
2410
|
+
close() {
|
|
2411
|
+
handleClose();
|
|
2412
|
+
},
|
|
2413
|
+
toggle() {
|
|
2414
|
+
handleToggle();
|
|
2415
|
+
},
|
|
2416
|
+
destroy() {
|
|
2417
|
+
unsubscribe();
|
|
2418
|
+
cancelAnimationFrame(measureRaf);
|
|
2419
|
+
measureObs.disconnect();
|
|
2420
|
+
currentAnim?.cancel();
|
|
2421
|
+
highlightAnim?.cancel();
|
|
2422
|
+
rootEl.removeEventListener("mouseenter", onMouseEnter);
|
|
2423
|
+
rootEl.removeEventListener("mouseleave", onMouseLeave);
|
|
2424
|
+
rootEl.removeEventListener("mouseover", onItemEnter);
|
|
2425
|
+
rootEl.removeEventListener("click", onClick);
|
|
2426
|
+
measureEl.remove();
|
|
2427
|
+
rootEl.remove();
|
|
2428
|
+
document.documentElement.style.removeProperty("--fluix-notch-offset");
|
|
2429
|
+
},
|
|
2430
|
+
update(opts) {
|
|
2431
|
+
if (opts.trigger !== void 0) trigger = opts.trigger;
|
|
2432
|
+
if (opts.position !== void 0) position = opts.position;
|
|
2433
|
+
if (opts.spring !== void 0) spring = opts.spring;
|
|
2434
|
+
if (opts.dotSize !== void 0) dotSize = opts.dotSize;
|
|
2435
|
+
if (opts.roundness !== void 0) roundness = opts.roundness;
|
|
2436
|
+
if (opts.theme !== void 0) theme = opts.theme;
|
|
2437
|
+
if (opts.fill !== void 0) fill = opts.fill;
|
|
2438
|
+
if (opts.open !== void 0) controlledOpen = opts.open;
|
|
2439
|
+
if (opts.onOpenChange !== void 0) onOpenChange = opts.onOpenChange;
|
|
2440
|
+
if (opts.pill !== void 0) {
|
|
2441
|
+
pillDiv.textContent = "";
|
|
2442
|
+
pillDiv.appendChild(resolveContent(opts.pill));
|
|
2443
|
+
}
|
|
2444
|
+
if (opts.content !== void 0) {
|
|
2445
|
+
contentDiv.textContent = "";
|
|
2446
|
+
contentDiv.appendChild(resolveContent(opts.content));
|
|
2447
|
+
measureEl.textContent = "";
|
|
2448
|
+
measureEl.appendChild(resolveContent(opts.content).cloneNode(true));
|
|
2449
|
+
}
|
|
2450
|
+
pillDiv.style.width = `${dotSize}px`;
|
|
2451
|
+
pillDiv.style.height = `${dotSize}px`;
|
|
2452
|
+
machine2.configure({ position, trigger, roundness, fill, spring });
|
|
2453
|
+
if (controlledOpen !== void 0) {
|
|
2454
|
+
if (controlledOpen && !snapshot.open) machine2.open();
|
|
2455
|
+
else if (!controlledOpen && snapshot.open) machine2.close();
|
|
2456
|
+
}
|
|
2457
|
+
rootEl.removeEventListener("mouseenter", onMouseEnter);
|
|
2458
|
+
rootEl.removeEventListener("mouseleave", onMouseLeave);
|
|
2459
|
+
rootEl.removeEventListener("click", onClick);
|
|
2460
|
+
rootEl.addEventListener("mouseenter", onMouseEnter);
|
|
2461
|
+
rootEl.addEventListener("mouseleave", onMouseLeave);
|
|
2462
|
+
rootEl.addEventListener("click", onClick);
|
|
2463
|
+
updateLayout();
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// src/menu.ts
|
|
2469
|
+
var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
2470
|
+
var GOO_MATRIX = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10";
|
|
2471
|
+
function applyAttrs3(el, attrs) {
|
|
2472
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
2473
|
+
el.setAttribute(key, value);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
function createMenu(container, options) {
|
|
2477
|
+
let {
|
|
2478
|
+
orientation = MENU_DEFAULTS.orientation,
|
|
2479
|
+
variant = "pill",
|
|
2480
|
+
theme = "dark",
|
|
2481
|
+
activeId: controlledActiveId,
|
|
2482
|
+
onActiveChange,
|
|
2483
|
+
spring,
|
|
2484
|
+
roundness = MENU_DEFAULTS.roundness,
|
|
2485
|
+
blur: blurProp,
|
|
2486
|
+
fill,
|
|
2487
|
+
items
|
|
2488
|
+
} = options;
|
|
2489
|
+
const springConfig = () => spring ?? FLUIX_SPRING;
|
|
2490
|
+
const resolvedBlur = () => blurProp ?? Math.min(10, Math.max(6, roundness * 0.45));
|
|
2491
|
+
const machine2 = createMenuMachine({
|
|
2492
|
+
orientation,
|
|
2493
|
+
variant,
|
|
2494
|
+
spring,
|
|
2495
|
+
roundness,
|
|
2496
|
+
blur: blurProp,
|
|
2497
|
+
fill,
|
|
2498
|
+
initialActiveId: controlledActiveId ?? null
|
|
2499
|
+
});
|
|
2500
|
+
let snapshot = machine2.store.getSnapshot();
|
|
2501
|
+
let lastActiveNotified = snapshot.activeId;
|
|
2502
|
+
const attrs = getMenuAttrs({ orientation, theme, variant });
|
|
2503
|
+
const filterId = `fluix-menu-goo-${Math.random().toString(36).slice(2, 8)}`;
|
|
2504
|
+
const isTab = variant === "tab";
|
|
2505
|
+
const navEl = document.createElement("nav");
|
|
2506
|
+
applyAttrs3(navEl, attrs.root);
|
|
2507
|
+
navEl.setAttribute("aria-label", "Fluix menu");
|
|
2508
|
+
const canvasDiv = document.createElement("div");
|
|
2509
|
+
applyAttrs3(canvasDiv, attrs.canvas);
|
|
2510
|
+
const svg = document.createElementNS(SVG_NS3, "svg");
|
|
2511
|
+
svg.setAttribute("xmlns", SVG_NS3);
|
|
2512
|
+
svg.setAttribute("width", "1");
|
|
2513
|
+
svg.setAttribute("height", "1");
|
|
2514
|
+
svg.setAttribute("viewBox", "0 0 1 1");
|
|
2515
|
+
svg.setAttribute("aria-hidden", "true");
|
|
2516
|
+
let indicatorEl;
|
|
2517
|
+
if (isTab) {
|
|
2518
|
+
indicatorEl = document.createElementNS(SVG_NS3, "path");
|
|
2519
|
+
applyAttrs3(indicatorEl, attrs.indicator);
|
|
2520
|
+
indicatorEl.setAttribute("d", "");
|
|
2521
|
+
indicatorEl.setAttribute("opacity", "0");
|
|
2522
|
+
indicatorEl.setAttribute("fill", fill ?? "var(--fluix-menu-indicator)");
|
|
2523
|
+
svg.appendChild(indicatorEl);
|
|
2524
|
+
} else {
|
|
2525
|
+
const defs = document.createElementNS(SVG_NS3, "defs");
|
|
2526
|
+
const filter = document.createElementNS(SVG_NS3, "filter");
|
|
2527
|
+
filter.setAttribute("id", filterId);
|
|
2528
|
+
filter.setAttribute("x", "-20%");
|
|
2529
|
+
filter.setAttribute("y", "-20%");
|
|
2530
|
+
filter.setAttribute("width", "140%");
|
|
2531
|
+
filter.setAttribute("height", "140%");
|
|
2532
|
+
filter.setAttribute("color-interpolation-filters", "sRGB");
|
|
2533
|
+
const feBlur = document.createElementNS(SVG_NS3, "feGaussianBlur");
|
|
2534
|
+
feBlur.setAttribute("in", "SourceGraphic");
|
|
2535
|
+
feBlur.setAttribute("stdDeviation", String(resolvedBlur()));
|
|
2536
|
+
feBlur.setAttribute("result", "blur");
|
|
2537
|
+
const feCM = document.createElementNS(SVG_NS3, "feColorMatrix");
|
|
2538
|
+
feCM.setAttribute("in", "blur");
|
|
2539
|
+
feCM.setAttribute("type", "matrix");
|
|
2540
|
+
feCM.setAttribute("values", GOO_MATRIX);
|
|
2541
|
+
feCM.setAttribute("result", "goo");
|
|
2542
|
+
const feComp = document.createElementNS(SVG_NS3, "feComposite");
|
|
2543
|
+
feComp.setAttribute("in", "SourceGraphic");
|
|
2544
|
+
feComp.setAttribute("in2", "goo");
|
|
2545
|
+
feComp.setAttribute("operator", "atop");
|
|
2546
|
+
filter.appendChild(feBlur);
|
|
2547
|
+
filter.appendChild(feCM);
|
|
2548
|
+
filter.appendChild(feComp);
|
|
2549
|
+
defs.appendChild(filter);
|
|
2550
|
+
svg.appendChild(defs);
|
|
2551
|
+
const gGroup = document.createElementNS(SVG_NS3, "g");
|
|
2552
|
+
gGroup.setAttribute("filter", `url(#${filterId})`);
|
|
2553
|
+
indicatorEl = document.createElementNS(SVG_NS3, "rect");
|
|
2554
|
+
applyAttrs3(indicatorEl, attrs.indicator);
|
|
2555
|
+
indicatorEl.setAttribute("x", "0");
|
|
2556
|
+
indicatorEl.setAttribute("y", "0");
|
|
2557
|
+
indicatorEl.setAttribute("width", "0");
|
|
2558
|
+
indicatorEl.setAttribute("height", "0");
|
|
2559
|
+
indicatorEl.setAttribute("rx", "0");
|
|
2560
|
+
indicatorEl.setAttribute("ry", "0");
|
|
2561
|
+
indicatorEl.setAttribute("opacity", "0");
|
|
2562
|
+
indicatorEl.setAttribute("fill", fill ?? "var(--fluix-menu-indicator)");
|
|
2563
|
+
gGroup.appendChild(indicatorEl);
|
|
2564
|
+
svg.appendChild(gGroup);
|
|
2565
|
+
}
|
|
2566
|
+
canvasDiv.appendChild(svg);
|
|
2567
|
+
navEl.appendChild(canvasDiv);
|
|
2568
|
+
const listDiv = document.createElement("div");
|
|
2569
|
+
applyAttrs3(listDiv, attrs.list);
|
|
2570
|
+
const buttonMap = /* @__PURE__ */ new Map();
|
|
2571
|
+
function createItemButton(item) {
|
|
2572
|
+
const btn = document.createElement("button");
|
|
2573
|
+
btn.type = "button";
|
|
2574
|
+
const active = snapshot.activeId === item.id;
|
|
2575
|
+
const itemAttrs = attrs.item({ id: item.id, active, disabled: item.disabled });
|
|
2576
|
+
applyAttrs3(btn, itemAttrs);
|
|
2577
|
+
if (item.disabled) btn.disabled = true;
|
|
2578
|
+
btn.textContent = item.label;
|
|
2579
|
+
btn.addEventListener("click", () => {
|
|
2580
|
+
if (item.disabled) return;
|
|
2581
|
+
if (controlledActiveId === void 0) {
|
|
2582
|
+
machine2.setActive(item.id);
|
|
2583
|
+
} else {
|
|
2584
|
+
onActiveChange?.(item.id);
|
|
2585
|
+
}
|
|
2586
|
+
});
|
|
2587
|
+
buttonMap.set(item.id, btn);
|
|
2588
|
+
listDiv.appendChild(btn);
|
|
2589
|
+
}
|
|
2590
|
+
for (const item of items) {
|
|
2591
|
+
createItemButton(item);
|
|
2592
|
+
}
|
|
2593
|
+
navEl.appendChild(listDiv);
|
|
2594
|
+
container.appendChild(navEl);
|
|
2595
|
+
let size = { width: 0, height: 0 };
|
|
2596
|
+
let measureRaf = 0;
|
|
2597
|
+
const measure = () => {
|
|
2598
|
+
const rect = navEl.getBoundingClientRect();
|
|
2599
|
+
const w = Math.ceil(rect.width);
|
|
2600
|
+
const h = Math.ceil(rect.height);
|
|
2601
|
+
if (size.width !== w || size.height !== h) {
|
|
2602
|
+
size = { width: w, height: h };
|
|
2603
|
+
updateSvgSize();
|
|
2604
|
+
connection?.sync(false);
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
const resizeObs = new ResizeObserver(() => {
|
|
2608
|
+
cancelAnimationFrame(measureRaf);
|
|
2609
|
+
measureRaf = requestAnimationFrame(measure);
|
|
2610
|
+
});
|
|
2611
|
+
resizeObs.observe(navEl);
|
|
2612
|
+
function updateSvgSize() {
|
|
2613
|
+
const w = Math.max(1, size.width);
|
|
2614
|
+
const h = Math.max(1, size.height);
|
|
2615
|
+
svg.setAttribute("width", String(w));
|
|
2616
|
+
svg.setAttribute("height", String(h));
|
|
2617
|
+
svg.setAttribute("viewBox", `0 0 ${w} ${h}`);
|
|
2618
|
+
}
|
|
2619
|
+
let connection = connectMenu({
|
|
2620
|
+
root: navEl,
|
|
2621
|
+
indicator: indicatorEl,
|
|
2622
|
+
getActiveId: () => snapshot.activeId,
|
|
2623
|
+
onSelect(id) {
|
|
2624
|
+
if (controlledActiveId === void 0) {
|
|
2625
|
+
machine2.setActive(id);
|
|
2626
|
+
} else {
|
|
2627
|
+
onActiveChange?.(id);
|
|
2628
|
+
}
|
|
2629
|
+
},
|
|
2630
|
+
spring: springConfig(),
|
|
2631
|
+
variant,
|
|
2632
|
+
orientation
|
|
2633
|
+
});
|
|
2634
|
+
requestAnimationFrame(() => {
|
|
2635
|
+
measure();
|
|
2636
|
+
connection.sync(false);
|
|
2637
|
+
});
|
|
2638
|
+
const unsubscribe = machine2.store.subscribe(() => {
|
|
2639
|
+
const next = machine2.store.getSnapshot();
|
|
2640
|
+
snapshot = next;
|
|
2641
|
+
for (const item of items) {
|
|
2642
|
+
const btn = buttonMap.get(item.id);
|
|
2643
|
+
if (btn) {
|
|
2644
|
+
const active = next.activeId === item.id;
|
|
2645
|
+
const itemAttrs = attrs.item({ id: item.id, active, disabled: item.disabled });
|
|
2646
|
+
applyAttrs3(btn, itemAttrs);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
if (next.activeId && lastActiveNotified !== next.activeId && onActiveChange) {
|
|
2650
|
+
onActiveChange(next.activeId);
|
|
2651
|
+
}
|
|
2652
|
+
lastActiveNotified = next.activeId;
|
|
2653
|
+
connection.sync(false);
|
|
2654
|
+
});
|
|
2655
|
+
return {
|
|
2656
|
+
setActive(id) {
|
|
2657
|
+
machine2.setActive(id);
|
|
2658
|
+
},
|
|
2659
|
+
update(opts) {
|
|
2660
|
+
if (opts.orientation !== void 0) orientation = opts.orientation;
|
|
2661
|
+
if (opts.variant !== void 0) variant = opts.variant;
|
|
2662
|
+
if (opts.theme !== void 0) theme = opts.theme;
|
|
2663
|
+
if (opts.activeId !== void 0) controlledActiveId = opts.activeId;
|
|
2664
|
+
if (opts.onActiveChange !== void 0) onActiveChange = opts.onActiveChange;
|
|
2665
|
+
if (opts.spring !== void 0) spring = opts.spring;
|
|
2666
|
+
if (opts.roundness !== void 0) roundness = opts.roundness;
|
|
2667
|
+
if (opts.blur !== void 0) blurProp = opts.blur;
|
|
2668
|
+
if (opts.fill !== void 0) fill = opts.fill;
|
|
2669
|
+
machine2.configure({ orientation, variant, spring, roundness, blur: blurProp, fill });
|
|
2670
|
+
if (controlledActiveId !== void 0) {
|
|
2671
|
+
machine2.setActive(controlledActiveId ?? null);
|
|
2672
|
+
}
|
|
2673
|
+
const newAttrs = getMenuAttrs({ orientation, theme, variant });
|
|
2674
|
+
applyAttrs3(navEl, newAttrs.root);
|
|
2675
|
+
if (opts.items !== void 0) {
|
|
2676
|
+
items = opts.items;
|
|
2677
|
+
listDiv.innerHTML = "";
|
|
2678
|
+
buttonMap.clear();
|
|
2679
|
+
for (const item of items) {
|
|
2680
|
+
createItemButton(item);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
connection.destroy();
|
|
2684
|
+
connection = connectMenu({
|
|
2685
|
+
root: navEl,
|
|
2686
|
+
indicator: indicatorEl,
|
|
2687
|
+
getActiveId: () => snapshot.activeId,
|
|
2688
|
+
onSelect(id) {
|
|
2689
|
+
if (controlledActiveId === void 0) {
|
|
2690
|
+
machine2.setActive(id);
|
|
2691
|
+
} else {
|
|
2692
|
+
onActiveChange?.(id);
|
|
2693
|
+
}
|
|
2694
|
+
},
|
|
2695
|
+
spring: springConfig(),
|
|
2696
|
+
variant,
|
|
2697
|
+
orientation
|
|
2698
|
+
});
|
|
2699
|
+
requestAnimationFrame(() => {
|
|
2700
|
+
measure();
|
|
2701
|
+
connection.sync(false);
|
|
2702
|
+
});
|
|
2703
|
+
},
|
|
2704
|
+
destroy() {
|
|
2705
|
+
unsubscribe();
|
|
2706
|
+
cancelAnimationFrame(measureRaf);
|
|
2707
|
+
resizeObs.disconnect();
|
|
2708
|
+
connection.destroy();
|
|
2709
|
+
navEl.remove();
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
exports.createMenu = createMenu;
|
|
2715
|
+
exports.createNotch = createNotch;
|
|
1243
2716
|
exports.createToaster = createToaster;
|
|
1244
2717
|
exports.fluix = fluix;
|
|
1245
2718
|
|