[MM-36459] Drag and drop for dropdown (#1651)
* [MM-36459] Drag and drop for dropdown * CircleCI build * Drag and drop feedback from UX * PR feedback * PR feedback
This commit is contained in:
@@ -446,7 +446,7 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- /^release-\d+(\.\d+){1,2}(-rc.*)?/
|
- /^release-\d+(\.\d+){1,2}(-rc.*)?/
|
||||||
- pull/1647
|
- pull/1651
|
||||||
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
# for master/PR builds
|
# for master/PR builds
|
||||||
|
@@ -58,6 +58,13 @@ export const updateUnreads = (serverName: string, unreads: boolean) => {
|
|||||||
emitMentions(serverName);
|
emitMentions(serverName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateBadge = () => {
|
||||||
|
const expired = anyExpired();
|
||||||
|
const mentions = totalMentions();
|
||||||
|
const unreads = anyUnreads();
|
||||||
|
emitBadge(expired, mentions, unreads);
|
||||||
|
};
|
||||||
|
|
||||||
export const getUnreads = (serverName: string) => {
|
export const getUnreads = (serverName: string) => {
|
||||||
return status.unreads.get(serverName) || false;
|
return status.unreads.get(serverName) || false;
|
||||||
};
|
};
|
||||||
|
@@ -64,7 +64,7 @@ function showBadge(sessionExpired: boolean, mentionCount: number, showUnreadBadg
|
|||||||
|
|
||||||
export function setUnreadBadgeSetting(showUnreadBadge: boolean) {
|
export function setUnreadBadgeSetting(showUnreadBadge: boolean) {
|
||||||
showUnreadBadgeSetting = showUnreadBadge;
|
showUnreadBadgeSetting = showUnreadBadge;
|
||||||
AppState.emitStatus();
|
AppState.updateBadge();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupBadge() {
|
export function setupBadge() {
|
||||||
|
@@ -13,6 +13,7 @@ import {
|
|||||||
SWITCH_SERVER,
|
SWITCH_SERVER,
|
||||||
CLOSE_TEAMS_DROPDOWN,
|
CLOSE_TEAMS_DROPDOWN,
|
||||||
SHOW_NEW_SERVER_MODAL,
|
SHOW_NEW_SERVER_MODAL,
|
||||||
|
UPDATE_TEAMS,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
|
|
||||||
console.log('preloaded for the dropdown!');
|
console.log('preloaded for the dropdown!');
|
||||||
@@ -34,12 +35,15 @@ window.addEventListener('message', async (event) => {
|
|||||||
case CLOSE_TEAMS_DROPDOWN:
|
case CLOSE_TEAMS_DROPDOWN:
|
||||||
ipcRenderer.send(CLOSE_TEAMS_DROPDOWN);
|
ipcRenderer.send(CLOSE_TEAMS_DROPDOWN);
|
||||||
break;
|
break;
|
||||||
|
case UPDATE_TEAMS:
|
||||||
|
ipcRenderer.invoke(UPDATE_TEAMS, event.data.data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(`got a message: ${event}`);
|
console.log(`got a message: ${event}`);
|
||||||
console.log(event);
|
console.log(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on(UPDATE_TEAMS_DROPDOWN, (event, teams, activeTeam, darkMode, expired, mentions, unreads) => {
|
ipcRenderer.on(UPDATE_TEAMS_DROPDOWN, (event, teams, activeTeam, darkMode, hasGPOTeams, expired, mentions, unreads) => {
|
||||||
window.postMessage({type: UPDATE_TEAMS_DROPDOWN, data: {teams, activeTeam, darkMode, expired, mentions, unreads}}, window.location.href);
|
window.postMessage({type: UPDATE_TEAMS_DROPDOWN, data: {teams, activeTeam, darkMode, hasGPOTeams, expired, mentions, unreads}}, window.location.href);
|
||||||
});
|
});
|
||||||
|
@@ -25,6 +25,7 @@ export default class TeamDropdownView {
|
|||||||
teams: Team[];
|
teams: Team[];
|
||||||
activeTeam?: string;
|
activeTeam?: string;
|
||||||
darkMode: boolean;
|
darkMode: boolean;
|
||||||
|
hasGPOTeams?: boolean;
|
||||||
unreads?: Map<string, boolean>;
|
unreads?: Map<string, boolean>;
|
||||||
mentions?: Map<string, number>;
|
mentions?: Map<string, number>;
|
||||||
expired?: Map<string, boolean>;
|
expired?: Map<string, boolean>;
|
||||||
@@ -57,6 +58,7 @@ export default class TeamDropdownView {
|
|||||||
updateConfig = (event: IpcMainEvent, config: CombinedConfig) => {
|
updateConfig = (event: IpcMainEvent, config: CombinedConfig) => {
|
||||||
this.teams = config.teams;
|
this.teams = config.teams;
|
||||||
this.darkMode = config.darkMode;
|
this.darkMode = config.darkMode;
|
||||||
|
this.hasGPOTeams = config.registryTeams && config.registryTeams.length > 0;
|
||||||
this.updateDropdown();
|
this.updateDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ export default class TeamDropdownView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateDropdown = () => {
|
updateDropdown = () => {
|
||||||
this.view.webContents.send(UPDATE_TEAMS_DROPDOWN, this.teams, this.activeTeam, this.darkMode, this.expired, this.mentions, this.unreads);
|
this.view.webContents.send(UPDATE_TEAMS_DROPDOWN, this.teams, this.activeTeam, this.darkMode, this.hasGPOTeams, this.expired, this.mentions, this.unreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpen = () => {
|
handleOpen = () => {
|
||||||
|
Binary file not shown.
@@ -368,6 +368,8 @@
|
|||||||
|
|
||||||
<glyph glyph-name="credit-card-outline" unicode="󰆛" d="M833 517h-666v83h666m0-500h-666v250h666m0 333h-666c-47 0-84-37-84-83v-500a83 83 0 0 1 84-83h666a83 83 0 0 1 84 83v500c0 46-38 83-84 83z" horiz-adv-x="1000" />
|
<glyph glyph-name="credit-card-outline" unicode="󰆛" d="M833 517h-666v83h666m0-500h-666v250h666m0 333h-666c-47 0-84-37-84-83v-500a83 83 0 0 1 84-83h666a83 83 0 0 1 84 83v500c0 46-38 83-84 83z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="drag-vertical" unicode="󰇝" d="M375 725h83v-83h-83v83m167 0h83v-83h-83v83m-167-167h83v-83h-83v83m167 0h83v-83h-83v83m-167-166h83v-84h-83v84m167 0h83v-84h-83v84m-167-167h83v-83h-83v83m167 0h83v-83h-83v83m-167-167h83v-83h-83v83m167 0h83v-83h-83v83z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="view-grid-outline" unicode="󰈄" d="M125 392h333v333h-333m83-83h167v-167h-167m334-500h333v333h-333m83-83h167v-167h-167m-500-83h333v333h-333m83-83h167v-167h-167m334 667v-333h333v333m-83-250h-167v167h167z" horiz-adv-x="1000" />
|
<glyph glyph-name="view-grid-outline" unicode="󰈄" d="M125 392h333v333h-333m83-83h167v-167h-167m334-500h333v333h-333m83-83h167v-167h-167m-500-83h333v333h-333m83-83h167v-167h-167m334 667v-333h333v333m-83-250h-167v167h167z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="exit-to-app" unicode="󰈆" d="M792 725h-584c-46 0-83-37-83-83v-167h83v167h584v-584h-584v167h-83v-167c0-46 37-83 83-83h584c46 0 83 37 83 83v584c0 46-37 83-83 83m-372-524l59-59 209 208-209 208-59-58 108-108h-403v-84h403l-108-107z" horiz-adv-x="1000" />
|
<glyph glyph-name="exit-to-app" unicode="󰈆" d="M792 725h-584c-46 0-83-37-83-83v-167h83v167h584v-584h-584v167h-83v-167c0-46 37-83 83-83h584c46 0 83 37 83 83v584c0 46-37 83-83 83m-372-524l59-59 209 208-209 208-59-58 108-108h-403v-84h403l-108-107z" horiz-adv-x="1000" />
|
||||||
@@ -398,6 +400,8 @@
|
|||||||
|
|
||||||
<glyph glyph-name="minus-circle-outline" unicode="󰍷" d="M500 17c-184 0-333 149-333 333 0 184 149 333 333 333 184 0 333-149 333-333 0-184-149-333-333-333m0 750a417 417 0 0 1-417-417 417 417 0 0 1 417-417 417 417 0 0 1 417 417 417 417 0 0 1-417 417m-208-459h416v84h-416" horiz-adv-x="1000" />
|
<glyph glyph-name="minus-circle-outline" unicode="󰍷" d="M500 17c-184 0-333 149-333 333 0 184 149 333 333 333 184 0 333-149 333-333 0-184-149-333-333-333m0 750a417 417 0 0 1-417-417 417 417 0 0 1 417-417 417 417 0 0 1 417 417 417 417 0 0 1-417 417m-208-459h416v84h-416" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="open-in-new" unicode="󰏌" d="M583 725v-83h150l-410-410 59-59 410 410v-150h83v292m-83-667h-584v584h292v83h-292c-46 0-83-37-83-83v-584a83 83 0 0 1 83-83h584a83 83 0 0 1 83 83v292h-83v-292z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="pause" unicode="󰏤" d="M583 58h167v584h-167m-333-584h167v584h-167v-584z" horiz-adv-x="1000" />
|
<glyph glyph-name="pause" unicode="󰏤" d="M583 58h167v584h-167m-333-584h167v584h-167v-584z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="play" unicode="󰐊" d="M333 636v-583l459 291-459 292z" horiz-adv-x="1000" />
|
<glyph glyph-name="play" unicode="󰐊" d="M333 636v-583l459 291-459 292z" horiz-adv-x="1000" />
|
||||||
@@ -422,6 +426,8 @@
|
|||||||
|
|
||||||
<glyph glyph-name="infinity" unicode="󰛤" d="M775 574c124 0 225-99 225-224 0-123-101-224-225-224-60 0-117 24-159 66l-116 102-118-104c-40-41-97-64-157-64-124 0-225 101-225 224 0 123 101 224 225 224 60 0 117-23 159-66l116-102 118 104c40 41 97 64 157 64m-450-324l113 100-111 98c-29 28-64 43-102 43-78 0-142-63-142-141 0-78 64-141 142-141 38 0 73 15 100 41m350 200l-112-100 110-98c29-28 65-43 102-43 78 0 142 63 142 141 0 78-64 141-142 141-38 0-73-15-100-41z" horiz-adv-x="1000" />
|
<glyph glyph-name="infinity" unicode="󰛤" d="M775 574c124 0 225-99 225-224 0-123-101-224-225-224-60 0-117 24-159 66l-116 102-118-104c-40-41-97-64-157-64-124 0-225 101-225 224 0 123 101 224 225 224 60 0 117-23 159-66l116-102 118 104c40 41 97 64 157 64m-450-324l113 100-111 98c-29 28-64 43-102 43-78 0-142-63-142-141 0-78 64-141 142-141 38 0 73 15 100 41m350 200l-112-100 110-98c29-28 65-43 102-43 78 0 142 63 142 141 0 78-64 141-142 141-38 0-73-15-100-41z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="plus-box-outline" unicode="󰜄" d="M792 58v584h-584v-584h584m0 667a83 83 0 0 0 83-83v-584a83 83 0 0 0-83-83h-584a83 83 0 0 0-83 83v584c0 46 38 83 83 83h584m-334-167h84v-166h166v-84h-166v-166h-84v166h-166v84h166v166z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="arrow-right-bold-outline" unicode="󰧂" d="M458 183h-333v334h333v250l417-417-417-417v250m84 375v-125h-334v-166h334v-125l208 208-208 208z" horiz-adv-x="1000" />
|
<glyph glyph-name="arrow-right-bold-outline" unicode="󰧂" d="M458 183h-333v334h333v250l417-417-417-417v250m84 375v-125h-334v-166h334v-125l208 208-208 208z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="trash-can-outline" unicode="󰩺" d="M375 725v-42h-208v-83h41v-542a83 83 0 0 1 84-83h416a83 83 0 0 1 84 83v542h41v83h-208v42h-250m-83-125h416v-542h-416v542m83-83v-375h83v375h-83m167 0v-375h83v375h-83z" horiz-adv-x="1000" />
|
<glyph glyph-name="trash-can-outline" unicode="󰩺" d="M375 725v-42h-208v-83h41v-542a83 83 0 0 1 84-83h416a83 83 0 0 1 84 83v542h41v83h-208v42h-250m-83-125h416v-542h-416v542m83-83v-375h83v375h-83m167 0v-375h83v375h-83z" horiz-adv-x="1000" />
|
||||||
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 84 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,13 +1,12 @@
|
|||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
|
@font-face {
|
||||||
@font-face {
|
|
||||||
font-family: 'compass-icons';
|
font-family: 'compass-icons';
|
||||||
src: url('../assets/fonts/compass-icons/compass-icons.eot?45182295');
|
src: url('../assets/fonts/compass-icons/compass-icons.eot?81398164');
|
||||||
src: url('../assets/fonts/compass-icons/compass-icons.eot?45182295#iefix') format('embedded-opentype'),
|
src: url('../assets/fonts/compass-icons/compass-icons.eot?81398164#iefix') format('embedded-opentype'),
|
||||||
url('../assets/fonts/compass-icons/compass-icons.woff2?45182295') format('woff2'),
|
url('../assets/fonts/compass-icons/compass-icons.woff2?81398164') format('woff2'),
|
||||||
url('../assets/fonts/compass-icons/compass-icons.woff?45182295') format('woff'),
|
url('../assets/fonts/compass-icons/compass-icons.woff?81398164') format('woff'),
|
||||||
url('../assets/fonts/compass-icons/compass-icons.ttf?45182295') format('truetype'),
|
url('../assets/fonts/compass-icons/compass-icons.ttf?81398164') format('truetype'),
|
||||||
url('../assets/fonts/compass-icons/compass-icons.svg?45182295#compass-icons') format('svg');
|
url('../assets/fonts/compass-icons/compass-icons.svg?81398164#compass-icons') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@@ -17,11 +16,10 @@
|
|||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'compass-icons';
|
font-family: 'compass-icons';
|
||||||
src: url('../fonts/compass-icons/compass-icons.svg?45182295#compass-icons') format('svg');
|
src: url('../font/compass-icons.svg?81398164#compass-icons') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||||
font-family: "compass-icons";
|
font-family: "compass-icons";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -238,6 +236,7 @@
|
|||||||
.icon-content-copy:before { content: ''; } /* '\f018f' */
|
.icon-content-copy:before { content: ''; } /* '\f018f' */
|
||||||
.icon-send-outline:before { content: ''; } /* '\f0190' */
|
.icon-send-outline:before { content: ''; } /* '\f0190' */
|
||||||
.icon-credit-card-outline:before { content: ''; } /* '\f019b' */
|
.icon-credit-card-outline:before { content: ''; } /* '\f019b' */
|
||||||
|
.icon-drag-vertical:before { content: ''; } /* '\f01dd' */
|
||||||
.icon-view-grid-outline:before { content: ''; } /* '\f0204' */
|
.icon-view-grid-outline:before { content: ''; } /* '\f0204' */
|
||||||
.icon-exit-to-app:before { content: ''; } /* '\f0206' */
|
.icon-exit-to-app:before { content: ''; } /* '\f0206' */
|
||||||
.icon-file-outline:before { content: ''; } /* '\f0224' */
|
.icon-file-outline:before { content: ''; } /* '\f0224' */
|
||||||
@@ -253,6 +252,7 @@
|
|||||||
.icon-message-text-outline:before { content: ''; } /* '\f036a' */
|
.icon-message-text-outline:before { content: ''; } /* '\f036a' */
|
||||||
.icon-minus:before { content: ''; } /* '\f0374' */
|
.icon-minus:before { content: ''; } /* '\f0374' */
|
||||||
.icon-minus-circle-outline:before { content: ''; } /* '\f0377' */
|
.icon-minus-circle-outline:before { content: ''; } /* '\f0377' */
|
||||||
|
.icon-open-in-new:before { content: ''; } /* '\f03cc' */
|
||||||
.icon-pause:before { content: ''; } /* '\f03e4' */
|
.icon-pause:before { content: ''; } /* '\f03e4' */
|
||||||
.icon-play:before { content: ''; } /* '\f040a' */
|
.icon-play:before { content: ''; } /* '\f040a' */
|
||||||
.icon-radiobox-blank:before { content: ''; } /* '\f043d' */
|
.icon-radiobox-blank:before { content: ''; } /* '\f043d' */
|
||||||
@@ -265,6 +265,7 @@
|
|||||||
.icon-update:before { content: ''; } /* '\f06b0' */
|
.icon-update:before { content: ''; } /* '\f06b0' */
|
||||||
.icon-eye-off-outline:before { content: ''; } /* '\f06d1' */
|
.icon-eye-off-outline:before { content: ''; } /* '\f06d1' */
|
||||||
.icon-infinity:before { content: ''; } /* '\f06e4' */
|
.icon-infinity:before { content: ''; } /* '\f06e4' */
|
||||||
|
.icon-plus-box-outline:before { content: ''; } /* '\f0704' */
|
||||||
.icon-arrow-right-bold-outline:before { content: ''; } /* '\f09c2' */
|
.icon-arrow-right-bold-outline:before { content: ''; } /* '\f09c2' */
|
||||||
.icon-trash-can-outline:before { content: ''; } /* '\f0a7a' */
|
.icon-trash-can-outline:before { content: ''; } /* '\f0a7a' */
|
||||||
.icon-account-minus-outline:before { content: ''; } /* '\f0aec' */
|
.icon-account-minus-outline:before { content: ''; } /* '\f0aec' */
|
||||||
|
@@ -24,6 +24,15 @@ body {
|
|||||||
min-width: 354px;
|
min-width: 354px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.TeamDropdown__droppable {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.TeamDropdown__header {
|
.TeamDropdown__header {
|
||||||
padding: 6px 20px;
|
padding: 6px 20px;
|
||||||
|
|
||||||
@@ -45,19 +54,23 @@ body {
|
|||||||
.TeamDropdown__button {
|
.TeamDropdown__button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 8px 18px;
|
padding: 8px 18px 8px 7px;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-family: Open Sans;
|
font-family: Open Sans;
|
||||||
|
|
||||||
&:hover {
|
&:not(.anyDragging):hover {
|
||||||
background-color: rgba(61, 60, 64, 0.08);
|
background-color: rgba(61, 60, 64, 0.08);
|
||||||
|
|
||||||
.TeamDropdown__button-edit, .TeamDropdown__button-remove {
|
.TeamDropdown__button-edit, .TeamDropdown__button-remove {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.TeamDropdown__draggable-handle > i.icon-drag-vertical {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus, &:focus-within {
|
&:focus, &:focus-within {
|
||||||
@@ -68,6 +81,15 @@ body {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.TeamDropdown__draggable-handle > i.icon-drag-vertical {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging .TeamDropdown__draggable-handle {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
@@ -76,17 +98,43 @@ body {
|
|||||||
color: rgba(61, 60, 64, 0.56);
|
color: rgba(61, 60, 64, 0.56);
|
||||||
}
|
}
|
||||||
|
|
||||||
> i.icon-check {
|
&.active i.icon-check {
|
||||||
color: #166de0;
|
color: #166de0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> .TeamDropdown__draggable-handle > span, &.addServer > span {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: #3D3C40;
|
color: #3D3C40;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.addServer {
|
||||||
|
padding-left: 24px;
|
||||||
|
|
||||||
|
> i.icon-plus::before {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.TeamDropdown__draggable-handle {
|
||||||
|
cursor: pointer !important;
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
cursor: grabbing !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> i.icon-drag-vertical {
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(61, 60, 64, 0.32);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.TeamDropdown__badge {
|
.TeamDropdown__badge {
|
||||||
@@ -171,7 +219,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.TeamDropdown__button {
|
.TeamDropdown__button {
|
||||||
&:hover {
|
&:not(.anyDragging):hover {
|
||||||
background-color: rgba(221, 221, 221, 0.08);
|
background-color: rgba(221, 221, 221, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,11 +227,11 @@ body {
|
|||||||
background-color: rgba(1, 119, 231, 0.08);
|
background-color: rgba(1, 119, 231, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
> i.icon-server-variant, i.icon-plus {
|
&:not(.active) i, i.icon-drag-vertical {
|
||||||
color: rgba(221, 221, 221, 0.56);
|
color: rgba(221, 221, 221, 0.56);
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> .TeamDropdown__draggable-handle > span, &.addServer > span {
|
||||||
color: #DDD;
|
color: #DDD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,4 +243,8 @@ body {
|
|||||||
.TeamDropdown__button-edit > i {
|
.TeamDropdown__button-edit > i {
|
||||||
color: rgba(221, 221, 221, 0.56);
|
color: rgba(221, 221, 221, 0.56);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.TeamDropdown__draggable-handle > i.icon-drag-vertical::before {
|
||||||
|
color:rgba(221, 221, 221, 0.32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,10 +4,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import {DragDropContext, Draggable, DraggingStyle, Droppable, DropResult, NotDraggingStyle} from 'react-beautiful-dnd';
|
||||||
|
|
||||||
import {Team} from 'types/config';
|
import {Team} from 'types/config';
|
||||||
|
|
||||||
import {CLOSE_TEAMS_DROPDOWN, REQUEST_TEAMS_DROPDOWN_INFO, SEND_DROPDOWN_MENU_SIZE, SHOW_NEW_SERVER_MODAL, SWITCH_SERVER, UPDATE_TEAMS_DROPDOWN} from 'common/communication';
|
import {CLOSE_TEAMS_DROPDOWN, REQUEST_TEAMS_DROPDOWN_INFO, SEND_DROPDOWN_MENU_SIZE, SHOW_NEW_SERVER_MODAL, SWITCH_SERVER, UPDATE_TEAMS, UPDATE_TEAMS_DROPDOWN} from 'common/communication';
|
||||||
|
|
||||||
import './css/dropdown.scss';
|
import './css/dropdown.scss';
|
||||||
import './css/compass-icons.css';
|
import './css/compass-icons.css';
|
||||||
@@ -20,24 +21,39 @@ type State = {
|
|||||||
unreads?: Map<string, boolean>;
|
unreads?: Map<string, boolean>;
|
||||||
mentions?: Map<string, number>;
|
mentions?: Map<string, number>;
|
||||||
expired?: Map<string, boolean>;
|
expired?: Map<string, boolean>;
|
||||||
|
hasGPOTeams?: boolean;
|
||||||
|
isAnyDragging: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStyle(style?: DraggingStyle | NotDraggingStyle) {
|
||||||
|
if (style?.transform) {
|
||||||
|
const axisLockY = `translate(0px${style.transform.slice(style.transform.indexOf(','), style.transform.length)}`;
|
||||||
|
return {
|
||||||
|
...style,
|
||||||
|
transform: axisLockY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
}
|
||||||
class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
||||||
constructor(props: Record<string, never>) {
|
constructor(props: Record<string, never>) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {
|
||||||
|
isAnyDragging: false,
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener('message', this.handleMessageEvent);
|
window.addEventListener('message', this.handleMessageEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageEvent = (event: MessageEvent) => {
|
handleMessageEvent = (event: MessageEvent) => {
|
||||||
if (event.data.type === UPDATE_TEAMS_DROPDOWN) {
|
if (event.data.type === UPDATE_TEAMS_DROPDOWN) {
|
||||||
const {teams, activeTeam, darkMode, unreads, mentions, expired} = event.data.data;
|
const {teams, activeTeam, darkMode, hasGPOTeams, unreads, mentions, expired} = event.data.data;
|
||||||
this.setState({
|
this.setState({
|
||||||
teams,
|
teams,
|
||||||
orderedTeams: teams.concat().sort((a: Team, b: Team) => a.order - b.order),
|
orderedTeams: teams.concat().sort((a: Team, b: Team) => a.order - b.order),
|
||||||
activeTeam,
|
activeTeam,
|
||||||
darkMode,
|
darkMode,
|
||||||
|
hasGPOTeams,
|
||||||
unreads,
|
unreads,
|
||||||
mentions,
|
mentions,
|
||||||
expired,
|
expired,
|
||||||
@@ -53,8 +69,10 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
closeMenu = () => {
|
closeMenu = () => {
|
||||||
(document.activeElement as HTMLElement).blur();
|
if (!this.state.isAnyDragging) {
|
||||||
window.postMessage({type: CLOSE_TEAMS_DROPDOWN}, window.location.href);
|
(document.activeElement as HTMLElement).blur();
|
||||||
|
window.postMessage({type: CLOSE_TEAMS_DROPDOWN}, window.location.href);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preventPropogation = (event: React.MouseEvent<HTMLDivElement>) => {
|
preventPropogation = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
@@ -70,6 +88,39 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||||||
return team.name === this.state.activeTeam;
|
return team.name === this.state.activeTeam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDragStart = () => {
|
||||||
|
this.setState({isAnyDragging: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragEnd = (result: DropResult) => {
|
||||||
|
const removedIndex = result.source.index;
|
||||||
|
const addedIndex = result.destination?.index;
|
||||||
|
if (addedIndex === undefined || removedIndex === addedIndex) {
|
||||||
|
this.setState({isAnyDragging: false});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.state.teams) {
|
||||||
|
throw new Error('No config');
|
||||||
|
}
|
||||||
|
const teams = this.state.teams.concat();
|
||||||
|
const tabOrder = teams.map((team, index) => {
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
order: team.order,
|
||||||
|
};
|
||||||
|
}).sort((a, b) => (a.order - b.order));
|
||||||
|
|
||||||
|
const team = tabOrder.splice(removedIndex, 1);
|
||||||
|
const newOrder = addedIndex < this.state.teams.length ? addedIndex : this.state.teams.length - 1;
|
||||||
|
tabOrder.splice(newOrder, 0, team[0]);
|
||||||
|
|
||||||
|
tabOrder.forEach((t, order) => {
|
||||||
|
teams[t.index].order = order;
|
||||||
|
});
|
||||||
|
this.setState({teams, orderedTeams: teams.concat().sort((a: Team, b: Team) => a.order - b.order), isAnyDragging: false});
|
||||||
|
window.postMessage({type: UPDATE_TEAMS, data: teams}, window.location.href);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window.postMessage({type: REQUEST_TEAMS_DROPDOWN_INFO}, window.location.href);
|
window.postMessage({type: REQUEST_TEAMS_DROPDOWN_INFO}, window.location.href);
|
||||||
window.addEventListener('click', this.closeMenu);
|
window.addEventListener('click', this.closeMenu);
|
||||||
@@ -83,6 +134,12 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||||||
window.removeEventListener('click', this.closeMenu);
|
window.removeEventListener('click', this.closeMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleClickOnDragHandle = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (this.state.isAnyDragging) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -95,61 +152,106 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||||||
<span>{'Servers'}</span>
|
<span>{'Servers'}</span>
|
||||||
</div>
|
</div>
|
||||||
<hr className='TeamDropdown__divider'/>
|
<hr className='TeamDropdown__divider'/>
|
||||||
{this.state.orderedTeams?.map((team, index) => {
|
<DragDropContext
|
||||||
const sessionExpired = this.state.expired?.get(team.name);
|
onDragStart={this.onDragStart}
|
||||||
const hasUnreads = this.state.unreads?.get(team.name);
|
onDragEnd={this.onDragEnd}
|
||||||
const mentionCount = this.state.mentions?.get(team.name);
|
>
|
||||||
|
<Droppable
|
||||||
|
isDropDisabled={this.state.hasGPOTeams}
|
||||||
|
droppableId='TeamDropdown__droppable'
|
||||||
|
>
|
||||||
|
{(provided) => (
|
||||||
|
<div
|
||||||
|
className='TeamDropdown__droppable'
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.droppableProps}
|
||||||
|
>
|
||||||
|
{this.state.orderedTeams?.map((team, orderedIndex) => {
|
||||||
|
const index = this.state.teams?.indexOf(team);
|
||||||
|
|
||||||
let badgeDiv: React.ReactNode;
|
const sessionExpired = this.state.expired?.get(team.name);
|
||||||
if (sessionExpired) {
|
const hasUnreads = this.state.unreads?.get(team.name);
|
||||||
badgeDiv = (
|
const mentionCount = this.state.mentions?.get(team.name);
|
||||||
<div className='TeamDropdown__badge-expired'>
|
|
||||||
<i className='icon-alert-circle-outline'/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (mentionCount && mentionCount > 0) {
|
|
||||||
badgeDiv = (
|
|
||||||
<div className='TeamDropdown__badge-count'>
|
|
||||||
<span>{mentionCount > 99 ? '99+' : mentionCount}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (hasUnreads) {
|
|
||||||
badgeDiv = (
|
|
||||||
<div className='TeamDropdown__badge-dot'/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
let badgeDiv: React.ReactNode;
|
||||||
<button
|
if (sessionExpired) {
|
||||||
className={'TeamDropdown__button'}
|
badgeDiv = (
|
||||||
onClick={this.selectServer(team)}
|
<div className='TeamDropdown__badge-expired'>
|
||||||
key={index}
|
<i className='icon-alert-circle-outline'/>
|
||||||
>
|
</div>
|
||||||
{this.isActiveTeam(team) ? <i className='icon-check'/> : <i className='icon-server-variant'/>}
|
);
|
||||||
<span>{team.name}</span>
|
} else if (mentionCount && mentionCount > 0) {
|
||||||
<div className='TeamDropdown__indicators'>
|
badgeDiv = (
|
||||||
<button
|
<div className='TeamDropdown__badge-count'>
|
||||||
className='TeamDropdown__button-edit'
|
<span>{mentionCount > 99 ? '99+' : mentionCount}</span>
|
||||||
disabled={true}
|
</div>
|
||||||
>
|
);
|
||||||
<i className='icon-pencil-outline'/>
|
} else if (hasUnreads) {
|
||||||
</button>
|
badgeDiv = (
|
||||||
<button
|
<div className='TeamDropdown__badge-dot'/>
|
||||||
className='TeamDropdown__button-remove'
|
);
|
||||||
disabled={true}
|
}
|
||||||
>
|
|
||||||
<i className='icon-trash-can-outline'/>
|
return (
|
||||||
</button>
|
<Draggable
|
||||||
{badgeDiv && <div className='TeamDropdown__badge'>
|
key={index}
|
||||||
{badgeDiv}
|
draggableId={`TeamDropdown__draggable-${index}`}
|
||||||
</div>}
|
index={orderedIndex}
|
||||||
|
disableInteractiveElementBlocking={true}
|
||||||
|
>
|
||||||
|
{(provided, snapshot) => (
|
||||||
|
<button
|
||||||
|
className={classNames('TeamDropdown__button', {
|
||||||
|
dragging: snapshot.isDragging,
|
||||||
|
anyDragging: this.state.isAnyDragging,
|
||||||
|
active: this.isActiveTeam(team),
|
||||||
|
})}
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
onClick={this.selectServer(team)}
|
||||||
|
style={getStyle(provided.draggableProps.style)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames('TeamDropdown__draggable-handle', {
|
||||||
|
dragging: snapshot.isDragging,
|
||||||
|
})}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
onClick={this.handleClickOnDragHandle}
|
||||||
|
>
|
||||||
|
<i className='icon-drag-vertical'/>
|
||||||
|
{this.isActiveTeam(team) ? <i className='icon-check'/> : <i className='icon-server-variant'/>}
|
||||||
|
<span>{team.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className='TeamDropdown__indicators'>
|
||||||
|
<button
|
||||||
|
className='TeamDropdown__button-edit'
|
||||||
|
disabled={true}
|
||||||
|
>
|
||||||
|
<i className='icon-pencil-outline'/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='TeamDropdown__button-remove'
|
||||||
|
disabled={true}
|
||||||
|
>
|
||||||
|
<i className='icon-trash-can-outline'/>
|
||||||
|
</button>
|
||||||
|
{badgeDiv && <div className='TeamDropdown__badge'>
|
||||||
|
{badgeDiv}
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{provided.placeholder}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
)}
|
||||||
);
|
</Droppable>
|
||||||
})}
|
</DragDropContext>
|
||||||
<hr className='TeamDropdown__divider'/>
|
<hr className='TeamDropdown__divider'/>
|
||||||
<button
|
<button
|
||||||
className='TeamDropdown__button'
|
className='TeamDropdown__button addServer'
|
||||||
onClick={this.addServer}
|
onClick={this.addServer}
|
||||||
>
|
>
|
||||||
<i className='icon-plus'/>
|
<i className='icon-plus'/>
|
||||||
|
Reference in New Issue
Block a user