@@ -1495,6 +1495,12 @@ function RemoteFunctions(config = {}) {
14951495 <svg viewBox="0 0 20 16" fill="currentColor" width="17" height="17">
14961496 <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
14971497 </svg>
1498+ ` ,
1499+
1500+ verticalEllipsis : `
1501+ <svg viewBox="0 0 24 24" fill="currentColor">
1502+ <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
1503+ </svg>
14981504 `
14991505 } ;
15001506
@@ -1606,14 +1612,8 @@ function RemoteFunctions(config = {}) {
16061612 <span data-action="delete" title="${ config . strings . delete } ">
16071613 ${ ICONS . trash }
16081614 </span>
1609- <span data-action="cut" title='Cut'>
1610- ${ ICONS . cut }
1611- </span>
1612- <span data-action="copy" title='Copy'>
1613- ${ ICONS . copy }
1614- </span>
1615- <span data-action="paste" title='Paste'>
1616- ${ ICONS . paste }
1615+ <span data-action="more-options" title='More Options'>
1616+ ${ ICONS . verticalEllipsis }
16171617 </span>
16181618 </div>` ;
16191619
@@ -1719,9 +1719,20 @@ function RemoteFunctions(config = {}) {
17191719 event . preventDefault ( ) ;
17201720 // data-action is to differentiate between the buttons (duplicate, delete, select-parent etc)
17211721 const action = event . currentTarget . getAttribute ( 'data-action' ) ;
1722- handleOptionClick ( event , action , this . element ) ;
1723- if ( action !== 'duplicate' ) {
1724- this . remove ( ) ;
1722+
1723+ if ( action === 'more-options' ) {
1724+ // to toggle the dropdown on more options button click
1725+ if ( _moreOptionsDropdown ) {
1726+ _moreOptionsDropdown . remove ( ) ;
1727+ } else {
1728+ _moreOptionsDropdown = new MoreOptionsDropdown ( this . element , event . currentTarget ) ;
1729+ }
1730+ } else {
1731+ handleOptionClick ( event , action , this . element ) ;
1732+ // as we don't want to remove the options box on duplicate button click
1733+ if ( action !== 'duplicate' ) {
1734+ this . remove ( ) ;
1735+ }
17251736 }
17261737 } ) ;
17271738 } ) ;
@@ -1738,6 +1749,179 @@ function RemoteFunctions(config = {}) {
17381749 }
17391750 } ;
17401751
1752+ /**
1753+ * the more options dropdown which appears when user clicks on the ellipsis button in the options box
1754+ */
1755+ function MoreOptionsDropdown ( targetElement , ellipsisButton ) {
1756+ this . targetElement = targetElement ;
1757+ this . ellipsisButton = ellipsisButton ;
1758+ this . remove = this . remove . bind ( this ) ;
1759+ this . create ( ) ;
1760+ }
1761+
1762+ MoreOptionsDropdown . prototype = {
1763+ _getDropdownPosition : function ( dropdownWidth , dropdownHeight ) {
1764+ const buttonBounds = this . ellipsisButton . getBoundingClientRect ( ) ;
1765+ const viewportHeight = window . innerHeight ;
1766+
1767+ let topPos , leftPos ;
1768+
1769+ // Check if there's enough space below the button
1770+ const spaceBelow = viewportHeight - buttonBounds . bottom ;
1771+ const spaceAbove = buttonBounds . top ;
1772+
1773+ if ( spaceBelow >= dropdownHeight + 6 ) {
1774+ // Show below the ellipsis button
1775+ topPos = buttonBounds . bottom + window . pageYOffset + 6 ;
1776+ } else if ( spaceAbove >= dropdownHeight + 6 ) {
1777+ // Show above the ellipsis button
1778+ topPos = buttonBounds . top + window . pageYOffset - dropdownHeight - 6 ;
1779+ } else {
1780+ // Not enough space either way, default to below
1781+ topPos = buttonBounds . bottom + window . pageYOffset + 6 ;
1782+ }
1783+
1784+ // Align dropdown to the right edge of the button
1785+ leftPos = buttonBounds . right + window . pageXOffset - dropdownWidth ;
1786+
1787+ // Make sure dropdown doesn't go off the left edge of viewport
1788+ if ( leftPos < 0 ) {
1789+ leftPos = buttonBounds . left + window . pageXOffset ;
1790+ }
1791+
1792+ return { topPos : topPos , leftPos : leftPos } ;
1793+ } ,
1794+
1795+ _style : function ( ) {
1796+ this . body = window . document . createElement ( "div" ) ;
1797+ this . body . setAttribute ( "data-phcode-internal-c15r5a9" , "true" ) ;
1798+
1799+ const shadow = this . body . attachShadow ( { mode : "open" } ) ;
1800+
1801+ let content = `
1802+ <div class="more-options-dropdown">
1803+ <div class="dropdown-item" data-action="cut">
1804+ <span class="item-icon">${ ICONS . cut } </span>
1805+ <span class="item-label">Cut</span>
1806+ </div>
1807+ <div class="dropdown-item" data-action="copy">
1808+ <span class="item-icon">${ ICONS . copy } </span>
1809+ <span class="item-label">Copy</span>
1810+ </div>
1811+ <div class="dropdown-item" data-action="paste">
1812+ <span class="item-icon">${ ICONS . paste } </span>
1813+ <span class="item-label">Paste</span>
1814+ </div>
1815+ </div>
1816+ ` ;
1817+
1818+ let styles = `
1819+ :host {
1820+ all: initial !important;
1821+ }
1822+
1823+ .phoenix-dropdown {
1824+ background-color: #ffffff !important;
1825+ color: #1f2933 !important;
1826+ border: 1px solid #1a73e8 !important;
1827+ border-radius: 6px !important;
1828+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25) !important;
1829+ font-size: 13px !important;
1830+ font-family: Arial, sans-serif !important;
1831+ z-index: 2147483647 !important;
1832+ position: absolute !important;
1833+ left: -1000px;
1834+ top: -1000px;
1835+ box-sizing: border-box !important;
1836+ min-width: 150px !important;
1837+ padding: 4px 0 !important;
1838+ overflow: hidden !important;
1839+ }
1840+
1841+ .more-options-dropdown {
1842+ display: flex !important;
1843+ flex-direction: column !important;
1844+ }
1845+
1846+ .dropdown-item {
1847+ padding: 7px 14px !important;
1848+ cursor: pointer !important;
1849+ white-space: nowrap !important;
1850+ user-select: none !important;
1851+ display: flex !important;
1852+ align-items: center !important;
1853+ gap: 8px !important;
1854+ }
1855+
1856+ .dropdown-item:hover {
1857+ background-color: #e8f1ff !important;
1858+ }
1859+
1860+ .item-icon {
1861+ display: flex !important;
1862+ align-items: center !important;
1863+ justify-content: center !important;
1864+ width: 16px !important;
1865+ height: 16px !important;
1866+ flex-shrink: 0 !important;
1867+ }
1868+
1869+ .item-icon svg {
1870+ width: 16px !important;
1871+ height: 16px !important;
1872+ display: block !important;
1873+ }
1874+
1875+ .item-label {
1876+ flex: 1 !important;
1877+ }
1878+ ` ;
1879+
1880+ shadow . innerHTML = `<style>${ styles } </style><div class="phoenix-dropdown">${ content } </div>` ;
1881+ this . _shadow = shadow ;
1882+ } ,
1883+
1884+ create : function ( ) {
1885+ this . remove ( ) ;
1886+ this . _style ( ) ;
1887+ window . document . body . appendChild ( this . body ) ;
1888+
1889+ // to position the dropdown element at the right position
1890+ const dropdownElement = this . _shadow . querySelector ( '.phoenix-dropdown' ) ;
1891+ if ( dropdownElement ) {
1892+ const dropdownRect = dropdownElement . getBoundingClientRect ( ) ;
1893+ const pos = this . _getDropdownPosition ( dropdownRect . width , dropdownRect . height ) ;
1894+
1895+ dropdownElement . style . left = pos . leftPos + 'px' ;
1896+ dropdownElement . style . top = pos . topPos + 'px' ;
1897+ }
1898+
1899+ // click handlers for the dropdown items
1900+ const items = this . _shadow . querySelectorAll ( '.dropdown-item' ) ;
1901+ items . forEach ( item => {
1902+ item . addEventListener ( 'click' , ( event ) => {
1903+ event . stopPropagation ( ) ;
1904+ event . preventDefault ( ) ;
1905+ const action = event . currentTarget . getAttribute ( 'data-action' ) ;
1906+ handleOptionClick ( event , action , this . targetElement ) ;
1907+ // when an option is selected we close both the dropdown as well as the options box
1908+ this . remove ( ) ;
1909+ if ( _nodeMoreOptionsBox ) {
1910+ _nodeMoreOptionsBox . remove ( ) ;
1911+ }
1912+ } ) ;
1913+ } ) ;
1914+ } ,
1915+
1916+ remove : function ( ) {
1917+ if ( this . body && this . body . parentNode && this . body . parentNode === window . document . body ) {
1918+ window . document . body . removeChild ( this . body ) ;
1919+ this . body = null ;
1920+ _moreOptionsDropdown = null ;
1921+ }
1922+ }
1923+ } ;
1924+
17411925 // Node info box to display DOM node ID and classes on hover
17421926 function NodeInfoBox ( element ) {
17431927 this . element = element ;
@@ -3776,6 +3960,7 @@ function RemoteFunctions(config = {}) {
37763960 var _clickHighlight ;
37773961 var _nodeInfoBox ;
37783962 var _nodeMoreOptionsBox ;
3963+ var _moreOptionsDropdown ;
37793964 var _aiPromptBox ;
37803965 var _imageRibbonGallery ;
37813966 var _setup = false ;
@@ -4750,6 +4935,16 @@ function RemoteFunctions(config = {}) {
47504935 }
47514936 }
47524937
4938+ /**
4939+ * Helper function to dismiss MoreOptionsDropdown if it exists
4940+ */
4941+ function dismissMoreOptionsDropdown ( ) {
4942+ if ( _moreOptionsDropdown ) {
4943+ _moreOptionsDropdown . remove ( ) ;
4944+ _moreOptionsDropdown = null ;
4945+ }
4946+ }
4947+
47534948 /**
47544949 * Helper function to dismiss NodeInfoBox if it exists
47554950 */
@@ -4785,6 +4980,7 @@ function RemoteFunctions(config = {}) {
47854980 */
47864981 function dismissAllUIBoxes ( ) {
47874982 dismissNodeMoreOptionsBox ( ) ;
4983+ dismissMoreOptionsDropdown ( ) ;
47884984 dismissAIPromptBox ( ) ;
47894985 dismissNodeInfoBox ( ) ;
47904986 dismissImageRibbonGallery ( ) ;
0 commit comments