@rogieking/figui3 1.9.5 → 1.9.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/components.css CHANGED
@@ -666,7 +666,6 @@ input[type="text"][list] {
666
666
  ::picker(select) {
667
667
  position-area: auto;
668
668
  align-self: auto;
669
- position-try-fallbacks: none;
670
669
  max-block-size: 100vh;
671
670
  appearance: base-select;
672
671
  scrollbar-width: thin;
@@ -2080,38 +2079,8 @@ dialog,
2080
2079
  }
2081
2080
  }
2082
2081
  dialog[is="fig-dialog"] {
2083
- --offset: 1rem;
2084
- --translate-x: -50%;
2085
- --translate-y: -50%;
2086
2082
  --z-index: 999999;
2087
-
2088
- position: fixed;
2089
- top: 50%;
2090
- left: 50%;
2091
- margin: 0;
2092
2083
  z-index: var(--z-index);
2093
- transform: translate(var(--translate-x), var(--translate-y));
2094
-
2095
- &[position*="bottom"] {
2096
- bottom: var(--offset);
2097
- top: auto;
2098
- --translate-y: 0;
2099
- }
2100
- &[position*="top"] {
2101
- top: var(--offset);
2102
- bottom: auto;
2103
- --translate-y: 0;
2104
- }
2105
- &[position*="right"] {
2106
- right: var(--offset);
2107
- left: auto;
2108
- --translate-x: 0;
2109
- }
2110
- &[position*="left"] {
2111
- left: var(--offset);
2112
- right: auto;
2113
- --translate-x: 0;
2114
- }
2115
2084
  }
2116
2085
 
2117
2086
  /* Tooltip */
package/example.html CHANGED
@@ -25,6 +25,27 @@
25
25
 
26
26
  <h2><label>Effects/</label>UI3 Components</h2>
27
27
  <fig-spinner></fig-spinner>
28
+
29
+ <fig-button icon="true"
30
+ variant="ghost"
31
+ type="select"
32
+ style="position: absolute; right: 0;">
33
+ <svg width="24"
34
+ height="24"
35
+ viewBox="0 0 24 24"
36
+ fill="none"
37
+ xmlns="http://www.w3.org/2000/svg">
38
+ <path fill-rule="evenodd"
39
+ clip-rule="evenodd"
40
+ d="M9.64645 11.1464C9.84171 10.9512 10.1583 10.9512 10.3536 11.1464L12 12.7929L13.6464 11.1464C13.8417 10.9512 14.1583 10.9512 14.3536 11.1464C14.5488 11.3417 14.5488 11.6583 14.3536 11.8536L12.3536 13.8536C12.1583 14.0488 11.8417 14.0488 11.6464 13.8536L9.64645 11.8536C9.45118 11.6583 9.45118 11.3417 9.64645 11.1464Z"
41
+ fill="currentColor"
42
+ fill-opacity="0.9" />
43
+ </svg>
44
+ <fig-dropdown variant="neue">
45
+ <option>One if by sea</option>
46
+ <option>Two if by land</option>
47
+ </fig-dropdown>
48
+ </fig-button>
28
49
  </fig-header>
29
50
 
30
51
  <br /><br /><br /><br /><br /><br /><br /><br />
@@ -573,6 +594,7 @@
573
594
  </fig-field>
574
595
 
575
596
  <dialog open="true"
597
+ drag="true"
576
598
  position="bottom right"
577
599
  is="fig-dialog">
578
600
  <fig-header>
package/fig.js CHANGED
@@ -592,20 +592,214 @@ customElements.define("fig-popover", FigPopover);
592
592
  * @attr {boolean} modal - Whether the dialog should be modal
593
593
  */
594
594
  class FigDialog extends HTMLDialogElement {
595
+ #isDragging = false;
596
+ #dragOffset = { x: 0, y: 0 };
597
+ #boundPointerDown;
598
+ #boundPointerMove;
599
+ #boundPointerUp;
600
+ #offset = 16; // 1rem in pixels
601
+ #positionInitialized = false;
602
+
603
+ constructor() {
604
+ super();
605
+ this.#boundPointerDown = this.#handlePointerDown.bind(this);
606
+ this.#boundPointerMove = this.#handlePointerMove.bind(this);
607
+ this.#boundPointerUp = this.#handlePointerUp.bind(this);
608
+ }
609
+
595
610
  connectedCallback() {
596
611
  this.modal =
597
612
  this.hasAttribute("modal") && this.getAttribute("modal") !== "false";
613
+
614
+ // Set up drag functionality
615
+ this.drag =
616
+ this.hasAttribute("drag") && this.getAttribute("drag") !== "false";
617
+
598
618
  requestAnimationFrame(() => {
599
619
  this.#addCloseListeners();
620
+ this.#setupDragListeners();
621
+ this.#applyPosition();
600
622
  });
601
623
  }
602
624
 
625
+ disconnectedCallback() {
626
+ this.#removeDragListeners();
627
+ }
628
+
603
629
  #addCloseListeners() {
604
630
  this.querySelectorAll("fig-button[close-dialog]").forEach((button) => {
605
631
  button.removeEventListener("click", this.close);
606
632
  button.addEventListener("click", this.close.bind(this));
607
633
  });
608
634
  }
635
+
636
+ #applyPosition() {
637
+ const position = this.getAttribute("position") || "";
638
+ const rect = this.getBoundingClientRect();
639
+ const viewportWidth = window.innerWidth;
640
+ const viewportHeight = window.innerHeight;
641
+
642
+ // Default to centered
643
+ let top = (viewportHeight - rect.height) / 2;
644
+ let left = (viewportWidth - rect.width) / 2;
645
+
646
+ // Parse position attribute
647
+ const hasTop = position.includes("top");
648
+ const hasBottom = position.includes("bottom");
649
+ const hasLeft = position.includes("left");
650
+ const hasRight = position.includes("right");
651
+
652
+ // Vertical positioning
653
+ if (hasTop) {
654
+ top = this.#offset;
655
+ } else if (hasBottom) {
656
+ top = viewportHeight - rect.height - this.#offset;
657
+ }
658
+
659
+ // Horizontal positioning
660
+ if (hasLeft) {
661
+ left = this.#offset;
662
+ } else if (hasRight) {
663
+ left = viewportWidth - rect.width - this.#offset;
664
+ }
665
+
666
+ // Apply position using fixed positioning with pixels
667
+ this.style.position = "fixed";
668
+ this.style.top = `${top}px`;
669
+ this.style.left = `${left}px`;
670
+ this.style.transform = "none";
671
+ this.style.margin = "0";
672
+
673
+ this.#positionInitialized = true;
674
+ }
675
+
676
+ #setupDragListeners() {
677
+ if (this.drag) {
678
+ this.addEventListener("pointerdown", this.#boundPointerDown);
679
+ // Set move cursor only on fig-header elements
680
+ const header = this.querySelector("fig-header, header");
681
+ if (header) {
682
+ header.style.cursor = "move";
683
+ }
684
+ }
685
+ }
686
+
687
+ #removeDragListeners() {
688
+ this.removeEventListener("pointerdown", this.#boundPointerDown);
689
+ document.removeEventListener("pointermove", this.#boundPointerMove);
690
+ document.removeEventListener("pointerup", this.#boundPointerUp);
691
+ }
692
+
693
+ #isInteractiveElement(element) {
694
+ // List of interactive element types and attributes to avoid dragging on
695
+ const interactiveSelectors = [
696
+ "input",
697
+ "button",
698
+ "select",
699
+ "textarea",
700
+ "a",
701
+ "label",
702
+ '[contenteditable="true"]',
703
+ "[tabindex]",
704
+ "fig-button",
705
+ "fig-input-text",
706
+ "fig-input-number",
707
+ "fig-slider",
708
+ "fig-checkbox",
709
+ "fig-radio",
710
+ "fig-tab",
711
+ "fig-dropdown",
712
+ "fig-chit",
713
+ ];
714
+
715
+ // Check if the element itself is interactive
716
+ if (interactiveSelectors.some((selector) => element.matches?.(selector))) {
717
+ return true;
718
+ }
719
+
720
+ // Check if any parent element up to the dialog is interactive
721
+ let parent = element.parentElement;
722
+ while (parent && parent !== this) {
723
+ if (interactiveSelectors.some((selector) => parent.matches?.(selector))) {
724
+ return true;
725
+ }
726
+ parent = parent.parentElement;
727
+ }
728
+
729
+ return false;
730
+ }
731
+
732
+ #handlePointerDown(e) {
733
+ if (!this.drag || this.#isInteractiveElement(e.target)) {
734
+ return;
735
+ }
736
+
737
+ this.#isDragging = true;
738
+ this.setPointerCapture(e.pointerId);
739
+
740
+ // Get current position from computed style
741
+ const rect = this.getBoundingClientRect();
742
+
743
+ // Store offset from pointer to dialog top-left corner
744
+ this.#dragOffset.x = e.clientX - rect.left;
745
+ this.#dragOffset.y = e.clientY - rect.top;
746
+
747
+ document.addEventListener("pointermove", this.#boundPointerMove);
748
+ document.addEventListener("pointerup", this.#boundPointerUp);
749
+
750
+ e.preventDefault();
751
+ }
752
+
753
+ #handlePointerMove(e) {
754
+ if (!this.#isDragging) return;
755
+
756
+ // Calculate new position based on pointer position minus offset
757
+ const newLeft = e.clientX - this.#dragOffset.x;
758
+ const newTop = e.clientY - this.#dragOffset.y;
759
+
760
+ // Apply position directly with pixels
761
+ this.style.left = `${newLeft}px`;
762
+ this.style.top = `${newTop}px`;
763
+
764
+ e.preventDefault();
765
+ }
766
+
767
+ #handlePointerUp(e) {
768
+ if (!this.#isDragging) return;
769
+
770
+ this.#isDragging = false;
771
+ this.releasePointerCapture(e.pointerId);
772
+
773
+ document.removeEventListener("pointermove", this.#boundPointerMove);
774
+ document.removeEventListener("pointerup", this.#boundPointerUp);
775
+
776
+ e.preventDefault();
777
+ }
778
+
779
+ static get observedAttributes() {
780
+ return ["modal", "drag", "position"];
781
+ }
782
+
783
+ attributeChangedCallback(name, oldValue, newValue) {
784
+ if (name === "drag") {
785
+ this.drag = newValue !== null && newValue !== "false";
786
+
787
+ if (this.drag) {
788
+ this.#setupDragListeners();
789
+ } else {
790
+ this.#removeDragListeners();
791
+ // Remove move cursor from header
792
+ const header = this.querySelector("fig-header, header");
793
+ if (header) {
794
+ header.style.cursor = "";
795
+ }
796
+ }
797
+ }
798
+
799
+ if (name === "position" && this.#positionInitialized) {
800
+ this.#applyPosition();
801
+ }
802
+ }
609
803
  }
610
804
  customElements.define("fig-dialog", FigDialog, { extends: "dialog" });
611
805
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "1.9.5",
3
+ "version": "1.9.7",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "devDependencies": {