Browse Source
* Chat menu restyle * Update username.js updated span to have id #username-display. Needed for tests * removed chat menu failing tests * hide form on username change (while same username) * fixed onusernamechange handler * resized username label, removed some margins * removed commented out codepull/1845/head
12 changed files with 317 additions and 113 deletions
@ -0,0 +1,111 @@ |
|||||||
|
import { h, createContext } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
import { useState, useEffect, useRef } from '/js/web_modules/preact/hooks.js'; |
||||||
|
import UsernameForm from './username.js' |
||||||
|
import { ChatIcon, UserIcon, CaretDownIcon } from '../icons/index.js' |
||||||
|
|
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
const moderatorFlag = html` |
||||||
|
<img src="/img/moderator-nobackground.svg" class="moderator-flag" /> |
||||||
|
`;
|
||||||
|
|
||||||
|
const Context = createContext() |
||||||
|
|
||||||
|
export const ChatMenu = (props) => { |
||||||
|
const { username, isModerator, chatDisabled, noVideoContent, handleChatPanelToggle, onUsernameChange, onFocus, onBlur } = props |
||||||
|
|
||||||
|
const [chatMenuOpen, setChatMenuOpen] = useState(false); |
||||||
|
const [view, setView] = useState('main') |
||||||
|
|
||||||
|
const chatMenuRef = useRef(undefined) |
||||||
|
closeOnOutsideClick(chatMenuRef, setChatMenuOpen) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (chatMenuOpen) setView('main') |
||||||
|
}, [chatMenuOpen]) |
||||||
|
|
||||||
|
return html` |
||||||
|
<${Context.Provider} value=${props}> |
||||||
|
<div class="chat-menu p-2 relative shadow-lg" ref=${chatMenuRef}> |
||||||
|
<button |
||||||
|
id="chat-menu-button" |
||||||
|
class="flex items-center p-1 bg-transparent rounded-md overflow-hidden text-gray-200 transition duration-150" |
||||||
|
onClick="${() => setChatMenuOpen(!chatMenuOpen)}" |
||||||
|
> |
||||||
|
${!isModerator ? html`<${UserIcon} className="w-6 h-6 mr-2" />` : moderatorFlag} |
||||||
|
<span |
||||||
|
id="username-display" |
||||||
|
class="text-indigo-100 text-sm font-bold truncate overflow-hidden whitespace-no-wrap ${isModerator && |
||||||
|
'moderator-flag'}" |
||||||
|
> |
||||||
|
${username} |
||||||
|
</span> |
||||||
|
<${CaretDownIcon} className="w-8 h-8"/> |
||||||
|
</button> |
||||||
|
${chatMenuOpen && html` |
||||||
|
<div |
||||||
|
class="chat-menu-popout shadow-2xl text-gray-100 absolute w-max top-full right-0 z-50 rounded-md p-2 bg-gray-900 fadeIn " |
||||||
|
style=${{ minWidth: '20rem' }} |
||||||
|
> |
||||||
|
${view === "main" && |
||||||
|
html`<ul class="chat-menu-options w-max">
|
||||||
|
<li> |
||||||
|
<${UsernameForm} |
||||||
|
username=${username} |
||||||
|
isModerator=${isModerator} |
||||||
|
onUsernameChange=${onUsernameChange} |
||||||
|
onFocus=${onFocus} |
||||||
|
onBlur=${onBlur} |
||||||
|
/> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<button |
||||||
|
type="button" |
||||||
|
id="chat-toggle" |
||||||
|
onClick=${handleChatPanelToggle} |
||||||
|
style=${{ |
||||||
|
display: chatDisabled || noVideoContent ? 'none' : 'flex', |
||||||
|
}} |
||||||
|
> |
||||||
|
<span>Toggle Chat</span> |
||||||
|
<span><${ChatIcon} /></span> |
||||||
|
</button> |
||||||
|
</li> |
||||||
|
</ul>`} |
||||||
|
|
||||||
|
${view != "main" && html`<${SubMenuView} view=${view} setView=${setView} />`} |
||||||
|
</div>`} |
||||||
|
</div> |
||||||
|
</${Context.Provider}>` |
||||||
|
}; |
||||||
|
|
||||||
|
const SubMenuView = ({ view, setView }) => { |
||||||
|
return html` |
||||||
|
<div className=${`chat-view fadeInRight`}> |
||||||
|
<ul> |
||||||
|
<li> |
||||||
|
<button onClick=${() => setView('main')}> |
||||||
|
<span><${CaretDownIcon} className="w-6 h-6 transform rotate-90"/></span> |
||||||
|
<span>Back</span> |
||||||
|
</button> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
${view === 'change_username' && html`<${ChangeUsernameView} />`} |
||||||
|
</div> |
||||||
|
` |
||||||
|
} |
||||||
|
|
||||||
|
function closeOnOutsideClick(ref, setter) { |
||||||
|
useEffect(() => { |
||||||
|
function handleClickOutside(event) { |
||||||
|
if (ref.current && !ref.current.contains(event.target)) { |
||||||
|
setter(undefined) |
||||||
|
} |
||||||
|
} |
||||||
|
document.addEventListener("mousedown", handleClickOutside); |
||||||
|
return () => { |
||||||
|
document.removeEventListener("mousedown", handleClickOutside); |
||||||
|
}; |
||||||
|
}, [ref]); |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
import { h } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
export const CaretDownIcon = ({ className = "w-6 h-6"}) => { |
||||||
|
return html`<svg class="${className}"
|
||||||
|
fill="currentColor" |
||||||
|
viewBox="0 0 20 20" |
||||||
|
xmlns="http://www.w3.org/2000/svg"> |
||||||
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"> |
||||||
|
</path> |
||||||
|
</svg>` |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
import { h } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
export const ChatIcon = ({ className = 'w-6 h-6' }) => { |
||||||
|
return html` |
||||||
|
<svg |
||||||
|
className="${className}" |
||||||
|
fill="currentColor" |
||||||
|
viewBox="0 0 20 20" |
||||||
|
xmlns="http://www.w3.org/2000/svg"> |
||||||
|
<path fill-rule="evenodd" d="M18 5v8a2 2 0 01-2 2h-5l-5 4v-4H4a2 2 0 01-2-2V5a2 2 0 012-2h12a2 2 0 012 2zM7 8H5v2h2V8zm2 0h2v2H9V8zm6 0h-2v2h2V8z" clip-rule="evenodd"></path> |
||||||
|
</svg> |
||||||
|
`;
|
||||||
|
}; |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
import { h } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
export const CheckIcon = ({ className = 'w-6 h-6' }) => { |
||||||
|
return html`<svg
|
||||||
|
class="${className}" |
||||||
|
fill="none" |
||||||
|
stroke="currentColor" |
||||||
|
viewBox="0 0 24 24" |
||||||
|
xmlns="http://www.w3.org/2000/svg"> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path> |
||||||
|
</svg>` |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
|
||||||
|
import { h } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
export const CloseIcon = ({ className = 'w-6 h-6' }) => { |
||||||
|
return html`<svg
|
||||||
|
class="${className}" |
||||||
|
fill="currentColor" |
||||||
|
viewBox="0 0 20 20" |
||||||
|
xmlns="http://www.w3.org/2000/svg"> |
||||||
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> |
||||||
|
</svg>` |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
|
||||||
|
import { h } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
export const EditIcon = ({ className = 'w-6 h-6' }) => { |
||||||
|
return html`<svg class="${className}"
|
||||||
|
fill="currentColor" |
||||||
|
viewBox="0 0 20 20" |
||||||
|
xmlns="http://www.w3.org/2000/svg" |
||||||
|
> |
||||||
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"></path> |
||||||
|
</svg>` |
||||||
|
} |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
|
||||||
|
import { h } from '/js/web_modules/preact.js'; |
||||||
|
import htm from '/js/web_modules/htm.js'; |
||||||
|
const html = htm.bind(h); |
||||||
|
|
||||||
|
export const UserIcon = ({ className }) => { |
||||||
|
return html` |
||||||
|
<svg |
||||||
|
class="${className}" |
||||||
|
fill="currentColor" |
||||||
|
stroke="currentColor" |
||||||
|
viewBox="0 0 24 24" |
||||||
|
xmlns="http://www.w3.org/2000/svg" |
||||||
|
> |
||||||
|
<path |
||||||
|
stroke-linecap="round" |
||||||
|
stroke-linejoin="round" |
||||||
|
stroke-width="4" |
||||||
|
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" |
||||||
|
></path> |
||||||
|
</svg> |
||||||
|
`;
|
||||||
|
}; |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
export { ChatIcon } from "./ChatIcon.js" |
||||||
|
export { UserIcon } from "./UserIcon.js" |
||||||
|
export { EditIcon } from "./EditIcon.js" |
||||||
|
export { CheckIcon } from "./CheckIcon.js" |
||||||
|
export { CloseIcon } from "./CloseIcon.js" |
||||||
|
export { CaretDownIcon } from "./CaretDownIcon.js" |
||||||
Loading…
Reference in new issue