diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index 1129282b3..a5c61e12e 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -109,7 +109,7 @@ template(name="boardHeaderBar") | ❌ a.board-header-btn.js-open-search-view(title="{{_ 'search'}}") - | 🔍 + span.emoji-icon 🔍 unless currentBoard.isTemplatesBoard a.board-header-btn.js-toggle-board-view( diff --git a/client/components/boards/boardsList.css b/client/components/boards/boardsList.css index dc7efdd66..d85299078 100644 --- a/client/components/boards/boardsList.css +++ b/client/components/boards/boardsList.css @@ -647,6 +647,13 @@ font-size: 25px; color: #fff; } + +/* Prevent Grey Icons from affecting checkmarks in background color list */ +body.grey-icons-enabled .checkmark-no-grey { + filter: none !important; + -webkit-filter: none !important; +} + /* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */ .board-list.mobile-view { height: calc(100vh - 120px); diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade index 5cf488c55..1003d183a 100644 --- a/client/components/boards/boardsList.jade +++ b/client/components/boards/boardsList.jade @@ -8,18 +8,26 @@ template(name="boardList") ul.menu li(class="menu-item {{#if isSelectedMenu 'starred'}}active{{/if}}") a.js-select-menu(data-type="starred") - span.menu-label ⭐ {{_ 'allboards.starred'}} + span.menu-label + span.emoji-icon ⭐ + | {{_ 'allboards.starred'}} span.menu-count {{menuItemCount 'starred'}} li(class="menu-item {{#if isSelectedMenu 'templates'}}active{{/if}}") a.js-select-menu(data-type="templates") - span.menu-label 📋 {{_ 'allboards.templates'}} + span.menu-label + span.emoji-icon 📋 + | {{_ 'allboards.templates'}} span.menu-count {{menuItemCount 'templates'}} li(class="menu-item {{#if isSelectedMenu 'remaining'}}active{{/if}}") a.js-select-menu(data-type="remaining") - span.menu-label 📂 {{_ 'allboards.remaining'}} + span.menu-label + span.emoji-icon 📂 + | {{_ 'allboards.remaining'}} span.menu-count {{menuItemCount 'remaining'}} .workspaces-header - span đŸ—‚ī¸ {{_ 'allboards.workspaces'}} + span + span.emoji-icon đŸ—‚ī¸ + | {{_ 'allboards.workspaces'}} a.js-add-workspace(title="{{_ 'allboards.add-workspace'}}") + // Workspaces tree +workspaceTree(nodes=workspacesTree selectedWorkspaceId=selectedWorkspaceId) @@ -43,44 +51,49 @@ template(name="boardList") li.AllBoardBtns div.AllBoardButtonsContainer if userHasOrgsOrTeams - span 🔍 + span.emoji-icon 🔍 input#filterBtn(type="button" value="{{_ 'filter'}}") button#resetBtn.filter-reset-btn - span.reset-icon ❌ + span.reset-icon + span.emoji-icon ❌ span {{_ 'filter-clear'}} // Right boards grid .boards-right-grid .boards-path-header .path-left - span.path-icon {{currentMenuPath.icon}} + span.path-icon.emoji-icon {{currentMenuPath.icon}} span.path-text {{currentMenuPath.text}} if BoardMultiSelection.isActive - span.multiselection-hint 📌 {{_ 'multi-selection-active'}} + span.multiselection-hint + span.emoji-icon 📌 + | {{_ 'multi-selection-active'}} .path-right if canModifyBoards if hasBoardsSelected button.js-archive-selected-boards.board-header-btn - span đŸ“Ļ + span.emoji-icon đŸ“Ļ span {{_ 'archive-board'}} button.js-duplicate-selected-boards.board-header-btn - span 📋 + span.emoji-icon 📋 span {{_ 'duplicate-board'}} a.board-header-btn.js-multiselection-activate( title="{{#if BoardMultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}" class="{{#if BoardMultiSelection.isActive}}emphasis{{/if}}") - | â˜‘ī¸ + span.emoji-icon â˜‘ī¸ if BoardMultiSelection.isActive a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}") - | ✖ + span.emoji-icon ✖ ul.board-list.clearfix.js-boards(class="{{#if isMiniScreen}}mobile-view{{/if}} {{#if BoardMultiSelection.isActive}}is-multiselection-active{{/if}}") li.js-add-board if isSelectedMenu 'templates' a.board-list-item.label(title="{{_ 'add-template-container'}}") - | ➕ {{_ 'add-template-container'}} + span.emoji-icon ➕ + | {{_ 'add-template-container'}} else a.board-list-item.label(title="{{_ 'add-board'}}") - | ➕ {{_ 'add-board'}} + span.emoji-icon ➕ + | {{_ 'add-board'}} each boards li.js-board(class="{{_id}} {{#if isStarred}}starred{{/if}} {{colorClass}} {{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}", draggable="true") if isInvited @@ -93,7 +106,8 @@ template(name="boardList") span.js-star-board( class="{{#if isStarred}}is-star-active{{else}}is-not-star-active{{/if}}" title="{{_ 'star-board-title'}}") - | {{#if isStarred}}⭐{{else}}☆{{/if}} + span.emoji-icon + | {{#if isStarred}}⭐{{else}}☆{{/if}} p.board-list-item-desc {{_ 'just-invited'}} button.js-accept-invite.primary {{_ 'accept'}} button.js-decline-invite {{_ 'decline'}} @@ -103,7 +117,9 @@ template(name="boardList") if BoardMultiSelection.isActive .materialCheckBox.multi-selection-checkbox.js-toggle-board-multi-selection( class="{{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}") - span.board-handle(title="{{_ 'drag-board'}}") â†•ī¸ + span.board-handle(title="{{_ 'drag-board'}}") + span.emoji-icon â†•ī¸ + a.js-open-board(href="{{pathFor 'board' id=_id slug=slug}}") span.details span.board-list-item-name(title="{{_ 'template-container'}}") @@ -116,17 +132,20 @@ template(name="boardList") span.js-has-spenttime-cards( class="{{#if hasOvertimeCards}}has-overtime-card-active{{else}}no-overtime-card-active{{/if}}" title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}") - | âąī¸ + span.emoji-icon âąī¸ span.js-star-board( class="{{#if isStarred}}is-star-active{{else}}is-not-star-active{{/if}}" title="{{_ 'star-board-title'}}") - | {{#if isStarred}}⭐{{else}}☆{{/if}} + span.emoji-icon + | {{#if isStarred}}⭐{{else}}☆{{/if}} else .board-list-item if BoardMultiSelection.isActive .materialCheckBox.multi-selection-checkbox.js-toggle-board-multi-selection( class="{{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}") - span.board-handle(title="{{_ 'drag-board'}}") â†•ī¸ + span.board-handle(title="{{_ 'drag-board'}}") + span.emoji-icon â†•ī¸ + a.js-open-board(href="{{pathFor 'board' id=_id slug=slug}}") span.details span.board-list-item-name(title="{{_ 'board-drag-drop-reorder-or-click-open'}}") @@ -151,11 +170,12 @@ template(name="boardList") span.js-has-spenttime-cards( class="{{#if hasOvertimeCards}}has-overtime-card-active{{else}}no-overtime-card-active{{/if}}" title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}") - | âąī¸ + span.emoji-icon âąī¸ a.js-star-board( class="{{#if isStarred}}is-star-active{{else}}is-not-star-active{{/if}}" title="{{_ 'star-board-title'}}") - | {{#if isStarred}}⭐{{else}}☆{{/if}} + span.emoji-icon + | {{#if isStarred}}⭐{{else}}☆{{/if}} template(name="boardListHeaderBar") h1 {{_ title }} @@ -174,16 +194,19 @@ template(name="workspaceTree") each nodes li.workspace-node(class="{{#if $eq id selectedWorkspaceId}}active{{/if}}" data-workspace-id="{{id}}" draggable="true") .workspace-node-content - span.workspace-drag-handle â†•ī¸ + span.workspace-drag-handle + span.emoji-icon â†•ī¸ + a.js-select-workspace(data-id="{{id}}") span.workspace-icon if icon +viewer = icon else - | 📁 + span.emoji-icon 📁 span.workspace-name= name - a.js-edit-workspace(data-id="{{id}}" title="{{_ 'allboards.edit-workspace'}}") âœī¸ + a.js-edit-workspace(data-id="{{id}}" title="{{_ 'allboards.edit-workspace'}}") + span.emoji-icon âœī¸ span.workspace-count {{workspaceCount id}} a.js-add-subworkspace(data-id="{{id}}" title="{{_ 'allboards.add-subworkspace'}}") + if children diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js index bb1d258d0..c3d973948 100644 --- a/client/components/boards/boardsList.js +++ b/client/components/boards/boardsList.js @@ -232,20 +232,18 @@ BlazeComponent.extendComponent({ }, boards() { let query = { - // { type: 'board' }, - // { type: { $in: ['board','template-container'] } }, $and: [ { archived: false }, { type: { $in: ['board', 'template-container'] } }, - { $or: [] }, { title: { $not: { $regex: /^\^.*\^$/ } } } ] }; + const membershipOrs = []; let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly'); if (FlowRouter.getRouteName() === 'home') { - query.$and[2].$or.push({ 'members.userId': Meteor.userId() }); + membershipOrs.push({ 'members.userId': Meteor.userId() }); if (allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue) { query.$and.push({ 'permission': 'private' }); @@ -260,7 +258,7 @@ BlazeComponent.extendComponent({ // } //query.$and[2].$or.push({'orgs': {$elemMatch : {orgId: orgsIds[0]}}}); - query.$and[2].$or.push({ 'orgs.orgId': { $in: orgsIds } }); + membershipOrs.push({ 'orgs.orgId': { $in: orgsIds } }); } let teamIdsUserBelongs = currUser?.teamIdsUserBelongs() || ''; @@ -270,8 +268,11 @@ BlazeComponent.extendComponent({ // query.$or[2].$or.push({'teams.teamId': teamsIds[i]}); // } //query.$and[2].$or.push({'teams': { $elemMatch : {teamId: teamsIds[0]}}}); - query.$and[2].$or.push({ 'teams.teamId': { $in: teamsIds } }); + membershipOrs.push({ 'teams.teamId': { $in: teamsIds } }); } + if (membershipOrs.length) { + query.$and.splice(2, 0, { $or: membershipOrs }); + } } else if (allowPrivateVisibilityOnly !== undefined && !allowPrivateVisibilityOnly.booleanValue) { query = { @@ -545,15 +546,18 @@ BlazeComponent.extendComponent({ const query = { $and: [ { archived: false }, - { type: 'board' }, - { $or: [] } + { type: 'board' } ] }; + const ors = []; if (selectedTeamsValues.length > 0) { - query.$and[2].$or.push({ 'teams.teamId': { $in: selectedTeamsValues } }); + ors.push({ 'teams.teamId': { $in: selectedTeamsValues } }); } if (selectedOrgsValues.length > 0) { - query.$and[2].$or.push({ 'orgs.orgId': { $in: selectedOrgsValues } }); + ors.push({ 'orgs.orgId': { $in: selectedOrgsValues } }); + } + if (ors.length) { + query.$and.push({ $or: ors }); } let filteredBoards = ReactiveCache.getBoards(query, {}); diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index bd59e4f85..112c17745 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -29,7 +29,7 @@ template(name="cardDetails") title="{{_ 'copy-card-link-to-clipboard'}}" href="{{ originRelativeUrl }}" ) - | 🔗 + span.emoji-icon 🔗 span.copied-tooltip {{_ 'copied'}} else unless isPopup @@ -43,7 +43,7 @@ template(name="cardDetails") title="{{_ 'copy-card-link-to-clipboard'}}" href="{{ originRelativeUrl }}" ) - | 🔗 + span.emoji-icon 🔗 span.copied-tooltip {{_ 'copied'}} h2.card-details-title.js-card-title( class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}") @@ -767,7 +767,7 @@ template(name="cardDetailsActionsPopup") ul.pop-over-list li a.js-more - | 🔗 + span.emoji-icon 🔗 | {{_ 'cardMorePopup-title'}} template(name="exportCardPopup") diff --git a/client/components/main/popup.css b/client/components/main/popup.css index dafbd2576..c0ad60e6d 100644 --- a/client/components/main/popup.css +++ b/client/components/main/popup.css @@ -94,24 +94,24 @@ } /* Admin edit popups: use full height */ -.pop-over[data-popup="editUser"], -.pop-over[data-popup="editOrg"], -.pop-over[data-popup="editTeam"] { +.pop-over[data-popup="editUserPopup"], +.pop-over[data-popup="editOrgPopup"], +.pop-over[data-popup="editTeamPopup"] { height: calc(100vh - 20px) !important; max-height: calc(100vh - 20px) !important; } -.pop-over[data-popup="editUser"] .content-wrapper, -.pop-over[data-popup="editOrg"] .content-wrapper, -.pop-over[data-popup="editTeam"] .content-wrapper { +.pop-over[data-popup="editUserPopup"] .content-wrapper, +.pop-over[data-popup="editOrgPopup"] .content-wrapper, +.pop-over[data-popup="editTeamPopup"] .content-wrapper { max-height: calc(100vh - 80px) !important; /* Subtract header height */ height: calc(100vh - 80px) !important; overflow-y: auto !important; } -.pop-over[data-popup="editUser"] .content-container, -.pop-over[data-popup="editOrg"] .content-container, -.pop-over[data-popup="editTeam"] .content-container { +.pop-over[data-popup="editUserPopup"] .content-container, +.pop-over[data-popup="editOrgPopup"] .content-container, +.pop-over[data-popup="editTeamPopup"] .content-container { max-height: calc(100vh - 80px) !important; /* Subtract header height */ height: calc(100vh - 80px) !important; } @@ -123,7 +123,7 @@ } /* Specific styling for language popup list */ -.pop-over[data-popup="changeLanguage"] .pop-over-list { +.pop-over[data-popup="changeLanguagePopup"] .pop-over-list { max-height: none; overflow: visible; height: auto; @@ -131,46 +131,69 @@ } /* Ensure content div in language popup contains all items */ -.pop-over[data-popup="changeLanguage"] .content { +.pop-over[data-popup="changeLanguagePopup"] .content { height: auto; - min-height: 100%; + /* Remove forced min-height to avoid top gap */ display: flex; flex-direction: column; } -/* Allow dynamic height for Change Language popup */ -.pop-over[data-popup="changeLanguage"] .content-wrapper { - max-height: inherit; /* Use dynamic height from JavaScript */ -} - -.pop-over[data-popup="changeLanguage"] .content-container { - max-height: inherit; /* Use dynamic height from JavaScript */ +/* Ensure hidden stack pages truly take no space */ +.pop-over[data-popup="changeLanguagePopup"] .content.no-height { + min-height: 0 !important; + height: 0 !important; + padding: 0 !important; + margin: 0 !important; + visibility: hidden !important; } /* Make language popup extend to bottom of browser window */ -.pop-over[data-popup="changeLanguage"] { - height: calc(100vh - 30px); - min-height: 300px; - /* Adjust positioning to move popup 30px higher */ - transform: translateY(-30px); +.pop-over[data-popup="changeLanguagePopup"] { + position: fixed !important; + bottom: 0 !important; + top: auto !important; + left: auto !important; + right: 20px !important; + width: auto !important; + max-width: 450px !important; + height: 100vh !important; + max-height: 100vh !important; + min-height: 300px !important; + display: flex !important; + flex-direction: column !important; + margin: 0 !important; } -.pop-over[data-popup="changeLanguage"] .content-wrapper { - height: calc(100% - 50px); /* Subtract header height more precisely */ - min-height: 250px; - overflow-y: auto; - max-height: none; /* Remove any max-height constraints */ - display: flex; - flex-direction: column; +/* Allow dynamic height for Change Language popup */ +.pop-over[data-popup="changeLanguagePopup"] .header { + flex-shrink: 0 !important; + height: auto !important; } -.pop-over[data-popup="changeLanguage"] .content-container { - height: auto; /* Let content determine height */ - min-height: 250px; - max-height: none; /* Remove any max-height constraints */ - flex: 1; - display: flex; - flex-direction: column; +.pop-over[data-popup="changeLanguagePopup"] .content-wrapper { + flex: 1 !important; + overflow-y: auto !important; + overflow-x: hidden !important; + min-height: 0 !important; + max-height: none !important; + height: auto !important; + width: 100% !important; +} + +.pop-over[data-popup="changeLanguagePopup"] .content-container { + height: auto !important; + max-height: none !important; + flex: 1 !important; + display: flex !important; + flex-direction: column !important; + width: 100% !important; +} + +.pop-over[data-popup="changeLanguagePopup"] .content { + height: auto !important; + max-height: none !important; + padding-bottom: 50px !important; + width: 100% !important; } /* Date popup sizing for native HTML inputs */ diff --git a/client/components/main/popup.tpl.jade b/client/components/main/popup.tpl.jade index bcc5756e4..630998962 100644 --- a/client/components/main/popup.tpl.jade +++ b/client/components/main/popup.tpl.jade @@ -2,6 +2,7 @@ class="{{#unless title}}miniprofile{{/unless}}" class=currentBoard.colorClass class="{{#unless title}}no-title{{/unless}}" + data-popup="{{popupName}}" style="left:{{offset.left}}px; top:{{offset.top}}px;{{#if offset.maxHeight}} max-height:{{offset.maxHeight}}px;{{/if}}") .header a.back-btn.js-back-view(class="{{#unless hasPopupParent}}is-hidden{{/unless}}") diff --git a/client/components/settings/adminReports.jade b/client/components/settings/adminReports.jade index e474ee465..c6dc7feb1 100644 --- a/client/components/settings/adminReports.jade +++ b/client/components/settings/adminReports.jade @@ -8,27 +8,27 @@ template(name="adminReports") ul li a.js-report-broken(data-id="report-broken") - | 🔗 + span.emoji-icon 🔗 | {{_ 'broken-cards'}} li a.js-report-files(data-id="report-files") - | 📎 + span.emoji-icon 📎 | {{_ 'filesReportTitle'}} li a.js-report-rules(data-id="report-rules") - | ✨ + span.emoji-icon ✨ | {{_ 'rulesReportTitle'}} li a.js-report-boards(data-id="report-boards") - | ✨ + span.emoji-icon ✨ | {{_ 'boardsReportTitle'}} li a.js-report-cards(data-id="report-cards") - | ✨ + span.emoji-icon ✨ | {{_ 'cardsReportTitle'}} .main-body diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index 0aa9a4dba..dda0197d9 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -48,7 +48,7 @@ template(name="people") option(value="inactive") {{_ 'admin-people-filter-inactive'}} option(value="admin") Admin button#unlockAllUsers.unlock-all-btn - | 🔓 + span.emoji-icon 🔓 | {{_ 'accounts-lockout-unlock-all'}} .ext-box-right span {{#unless isMiniScreen}}{{_ 'people-number'}}{{/unless}} #{peopleNumber} @@ -58,7 +58,7 @@ template(name="people") | {{_ 'add'}} / {{_ 'delete'}} {{_ 'teams'}} else if lockedUsersSetting.get span - span.text-red 🔒 + span.emoji-icon.text-red 🔒 unless isMiniScreen | {{_ 'accounts-lockout-locked-users'}} @@ -79,7 +79,7 @@ template(name="people") | {{_ 'people'}} li a.js-locked-users-menu(data-id="locked-users-setting") - span.text-red 🔒 + span.emoji-icon.text-red 🔒 | {{_ 'accounts-lockout-locked-users'}} .main-body if loading.get @@ -247,9 +247,9 @@ template(name="peopleRow") input.selectUserChkBox(type="checkbox", id="{{userData._id}}") td.account-status if isUserLocked - span.text-red.js-toggle-lock-status(data-user-id=userData._id, data-is-locked="true", title="{{_ 'accounts-lockout-click-to-unlock'}}") 🔒 + span.text-red.js-toggle-lock-status.emoji-icon(data-user-id=userData._id, data-is-locked="true", title="{{_ 'accounts-lockout-click-to-unlock'}}") 🔒 else - span.text-green.js-toggle-lock-status(data-user-id=userData._id, data-is-locked="false", title="{{_ 'accounts-lockout-user-unlocked'}}") 🔓 + span.text-green.js-toggle-lock-status.emoji-icon(data-user-id=userData._id, data-is-locked="false", title="{{_ 'accounts-lockout-user-unlocked'}}") 🔓 td.account-active-status if userData.loginDisabled span.text-red.js-toggle-active-status(data-user-id=userData._id, data-is-active="false", title="{{_ 'admin-people-user-inactive'}}") đŸšĢ diff --git a/client/components/settings/settingBody.jade b/client/components/settings/settingBody.jade index 1df865f38..d40916123 100644 --- a/client/components/settings/settingBody.jade +++ b/client/components/settings/settingBody.jade @@ -6,87 +6,87 @@ template(name="setting") .content-title.ext-box if isGeneralSetting span - | 🔑 + span.emoji-icon 🔑 | {{_ 'registration'}} else if isEmailSetting span - | âœ‰ī¸ + span.emoji-icon âœ‰ī¸ | {{_ 'email'}} else if isAccountSetting span - | đŸ‘Ĩ + span.emoji-icon đŸ‘Ĩ | {{_ 'accounts'}} else if isTableVisibilityModeSetting span - | đŸ‘ī¸ + span.emoji-icon đŸ‘ī¸ | {{_ 'tableVisibilityMode'}} else if isAnnouncementSetting span - | đŸ“ĸ + span.emoji-icon đŸ“ĸ | {{_ 'admin-announcement'}} else if isAccessibilitySetting span - | â™ŋ + span.emoji-icon â™ŋ | {{_ 'accessibility'}} else if isLayoutSetting span - | 🔗 + span.emoji-icon 🔗 | {{_ 'layout'}} else if isWebhookSetting span - | 🌐 + span.emoji-icon 🌐 | {{_ 'global-webhook'}} else if isAttachmentSettings span - | 📎 + span.emoji-iconpan.emoji-icon 📎 | {{_ 'attachments'}} else if isCronSettings span - | ⏰ + span.emoji-icon ⏰ | {{_ 'cron'}} .content-body .side-menu ul li(class="{{#if isGeneralSetting}}active{{/if}}") a.js-setting-menu(data-id="registration-setting") - | 🔑 + span.emoji-icon 🔑 | {{_ 'registration'}} unless isSandstorm li(class="{{#if isEmailSetting}}active{{/if}}") a.js-setting-menu(data-id="email-setting") - | âœ‰ī¸ + span.emoji-icon âœ‰ī¸ | {{_ 'email'}} li(class="{{#if isAccountSetting}}active{{/if}}") a.js-setting-menu(data-id="account-setting") - | đŸ‘Ĩ + span.emoji-icon đŸ‘Ĩ | {{_ 'accounts'}} li(class="{{#if isTableVisibilityModeSetting}}active{{/if}}") a.js-setting-menu(data-id="tableVisibilityMode-setting") - | đŸ‘ī¸ + span.emoji-icon đŸ‘ī¸ | {{_ 'tableVisibilityMode'}} li(class="{{#if isAnnouncementSetting}}active{{/if}}") a.js-setting-menu(data-id="announcement-setting") - | đŸ“ĸ + span.emoji-icon đŸ“ĸ | {{_ 'admin-announcement'}} li(class="{{#if isAccessibilitySetting}}active{{/if}}") a.js-setting-menu(data-id="accessibility-setting") - | â™ŋ + span.emoji-icon â™ŋ | {{_ 'accessibility'}} li(class="{{#if isLayoutSetting}}active{{/if}}") a.js-setting-menu(data-id="layout-setting") - | 🔗 + span.emoji-icon 🔗 | {{_ 'layout'}} li(class="{{#if isWebhookSetting}}active{{/if}}") a.js-setting-menu(data-id="webhook-setting") - | 🌐 + span.emoji-icon 🌐 | {{_ 'global-webhook'}} li(class="{{#if isAttachmentSettings}}active{{/if}}") a.js-setting-menu(data-id="attachment-settings") - | 📎 + span.emoji-icon 📎 | {{_ 'attachments'}} li(class="{{#if isCronSettings}}active{{/if}}") a.js-setting-menu(data-id="cron-settings") - | ⏰ + span.emoji-icon ⏰ | {{_ 'cron'}} .main-body if isLoading diff --git a/client/components/settings/settingHeader.jade b/client/components/settings/settingHeader.jade index cbbb9b03b..7ce77ba4e 100644 --- a/client/components/settings/settingHeader.jade +++ b/client/components/settings/settingHeader.jade @@ -5,31 +5,31 @@ template(name="settingHeaderBar") .setting-header-btns.left if currentUser a.setting-header-btn.settings(href="{{pathFor 'setting'}}") - | âš™ī¸ + span.emoji-icon âš™ī¸ span {{_ 'settings'}} a.setting-header-btn.people(href="{{pathFor 'people'}}") - | đŸ‘Ĩ + span.emoji-icon đŸ‘Ĩ span {{_ 'people'}} a.setting-header-btn.informations(href="{{pathFor 'admin-reports'}}") - | 📋 + span.emoji-icon 📋 span {{_ 'reports'}} a.setting-header-btn.informations(href="{{pathFor 'attachments'}}") - | 📎 + span.emoji-icon 📎 span {{_ 'attachments'}} a.setting-header-btn.informations(href="{{pathFor 'translation'}}") - | 🔤 + span.emoji-icon 🔤 span {{_ 'translation'}} a.setting-header-btn.informations(href="{{pathFor 'information'}}") - | â„šī¸ + span.emoji-icon â„šī¸ span {{_ 'info'}} else a.setting-header-btn.js-log-in( title="{{_ 'log-in'}}") - | đŸšĒ + span.emoji-icon đŸšĒ span {{_ 'log-in'}} diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index b7f11b816..4f02ea6d4 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -155,7 +155,7 @@ template(name="boardChangeColorPopup") span.background-box(class="board-color-{{this}}") span {{this}} if isSelected - | ✅ + span.checkmark-no-grey ✅ template(name="boardChangeBackgroundImagePopup") form @@ -575,7 +575,8 @@ template(name="boardMenuPopup") if currentUser.isBoardAdmin li a.js-open-rules-view(title="{{_ 'rules'}}") - | ✨ + span.emoji-icon + | ✨ | {{_ 'rules'}} if currentUser.isBoardAdmin li @@ -637,7 +638,8 @@ template(name="boardMenuPopup") // | {{_ 'delete-duplicate-lists'}} li a.js-archive-board - | âžĄī¸đŸ“Ļ + span.emoji-icon + | âžĄī¸đŸ“Ļ | {{_ 'archive-board'}} template(name="exportBoard") diff --git a/client/components/unicode-icons.css b/client/components/unicode-icons.css index 429c8c711..9afc50ccd 100644 --- a/client/components/unicode-icons.css +++ b/client/components/unicode-icons.css @@ -1,6 +1,33 @@ .unicode-icon { filter: grayscale(100%); + -webkit-filter: grayscale(100%); opacity: 0.8; display: inline-block; line-height: 1; } + +/* Greyscale for explicitly-marked emoji when feature is enabled */ +body.grey-icons-enabled .emoji-icon { + filter: grayscale(100%); + -webkit-filter: grayscale(100%); + opacity: 0.85; + display: inline-block; +} + +/* When grey icons are enabled, also grey common UI badges and toggles */ +body.grey-icons-enabled .card-date, +body.grey-icons-enabled .mobile-icon, +body.grey-icons-enabled .desktop-icon { + filter: grayscale(100%); + -webkit-filter: grayscale(100%); + opacity: 0.85; +} + +/* Grey card minibadges (icons + text + backgrounds) */ +body.grey-icons-enabled .minicard .badges .badge, +body.grey-icons-enabled .minicard .badges .badge .badge-icon, +body.grey-icons-enabled .minicard .badges .badge .badge-text { + filter: grayscale(100%); + -webkit-filter: grayscale(100%); + opacity: 0.9; +} diff --git a/client/components/unicode-icons.js b/client/components/unicode-icons.js index 2b9569b04..709d896cd 100644 --- a/client/components/unicode-icons.js +++ b/client/components/unicode-icons.js @@ -1,10 +1,12 @@ Meteor.startup(() => { const greyscaleIcons = [ 'đŸ”ŧ', '❌', 'đŸˇī¸', '📅', 'đŸ“Ĩ', '🚀', '👤', 'đŸ‘Ĩ', 'âœī¸', '📋', 'âœī¸', '🌐', '📎', '📝', '📋', '📜', '🏠', '🔒', '🔕', '🃏', - '⏰', '🛒', 'đŸ”ĸ', '✅', '❌', 'đŸ‘ī¸', '👍', '📋', '🕐', '🎨', + '⏰', '🛒', 'đŸ”ĸ', '✅', '❌', 'đŸ‘ī¸', '👍', '👎', '📋', '🕐', '🎨', '📤', 'âŦ†ī¸', 'âŦ‡ī¸', 'âžĄī¸', 'đŸ“Ļ', 'âŦ…ī¸', 'â†•ī¸', 'đŸ”Ŋ', '🔍', 'â–ŧ', '🏊', - '🔔', 'âš™ī¸', 'đŸ–ŧī¸', '🔑', 'đŸšĒ', 'â—€ī¸', 'âŒ¨ī¸', 'đŸ‘Ĩ', 'đŸˇī¸', '✅', 'đŸšĢ' + '🔔', 'âš™ī¸', 'đŸ–ŧī¸', '🔑', 'đŸšĒ', 'â—€ī¸', 'âŒ¨ī¸', 'đŸ‘Ĩ', 'đŸˇī¸', '✅', 'đŸšĢ', 'â˜‘ī¸', 'đŸ’Ŧ', + // Mobile/Desktop toggle + calendar + '📱', 'đŸ–Ĩī¸', 'đŸ—“ī¸' ]; const EXCLUDE_SELECTOR = '.header-user-bar-avatar, .avatar-initials, script, style'; @@ -34,43 +36,40 @@ Meteor.startup(() => { parent.replaceChild(span, textNode); } - function processNode(root) { + function wrapSubtree(root) { try { if (!root) return; - if (root.nodeType === Node.TEXT_NODE) { - wrapTextNodeOnce(root.parentNode, root); - return; + // Walk only within this subtree for text nodes + const walker = document.createTreeWalker( + root.nodeType === Node.ELEMENT_NODE ? root : root.parentNode || document.body, + NodeFilter.SHOW_TEXT, + { + acceptNode: (node) => { + if (!node || !node.nodeValue) return NodeFilter.FILTER_REJECT; + const parent = node.parentNode; + if (!parent || isExcluded(parent)) return NodeFilter.FILTER_REJECT; + if (parent.closest && parent.closest('.unicode-icon')) return NodeFilter.FILTER_REJECT; + const txt = node.nodeValue.trim(); + if (!txt || txt.length > 3) return NodeFilter.FILTER_REJECT; + return greyscaleIcons.includes(txt) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; + }, + }, + false, + ); + const toWrap = []; + while (walker.nextNode()) { + toWrap.push(walker.currentNode); } - if (root.nodeType !== Node.ELEMENT_NODE) return; - if (isExcluded(root)) return; - // Fast path: only check direct text children first - const children = Array.from(root.childNodes); - for (const child of children) { - if (child.nodeType === Node.TEXT_NODE) { - wrapTextNodeOnce(root, child); - } - } - // If element is small, also scan one level deeper to catch common structures - if (children.length <= 20) { - for (const child of children) { - if (child.nodeType === Node.ELEMENT_NODE && !isExcluded(child)) { - for (const gchild of Array.from(child.childNodes)) { - if (gchild.nodeType === Node.TEXT_NODE) wrapTextNodeOnce(child, gchild); - } - } - } + for (const textNode of toWrap) { + wrapTextNodeOnce(textNode.parentNode, textNode); } } catch (_) {} } function processInitial() { // Process only frequently used UI containers to avoid full-page walks - const roots = [ - document.body, - document.querySelector('#header-user-bar'), - ...Array.from(document.querySelectorAll('.pop-over, .pop-over-list, .board-header, .card-details, .sidebar-content')), - ].filter(Boolean); - roots.forEach(processNode); + const roots = [document.body].filter(Boolean); + roots.forEach(wrapSubtree); } function startObserver() { @@ -80,8 +79,8 @@ Meteor.startup(() => { for (const m of mutations) { if (m.type !== 'childList') continue; m.addedNodes && m.addedNodes.forEach((n) => { - // Avoid scanning huge subtrees repeatedly by limiting depth - processNode(n); + // Process only within the newly added subtree + wrapSubtree(n); }); } }); @@ -98,6 +97,7 @@ Meteor.startup(() => { function enableGrey() { if (enabled) return; enabled = true; + try { document.body.classList.add('grey-icons-enabled'); } catch (_) {} Meteor.defer(processInitial); startObserver(); } @@ -106,6 +106,7 @@ Meteor.startup(() => { if (!enabled) return; enabled = false; stopObserver(); + try { document.body.classList.remove('grey-icons-enabled'); } catch (_) {} // unwrap existing document.querySelectorAll('span.unicode-icon').forEach((span) => { const txt = document.createTextNode(span.textContent || ''); diff --git a/client/components/users/userHeader.jade b/client/components/users/userHeader.jade index f46f442d0..49724e84b 100644 --- a/client/components/users/userHeader.jade +++ b/client/components/users/userHeader.jade @@ -175,7 +175,7 @@ template(name="changeLanguagePopup") each languages li(class="{{# if isCurrentLanguage}}active{{/if}}") a.js-set-language - | {{languageFlag}} + span.emoji-icon {{languageFlag}} | {{name}} if isCurrentLanguage | âœ