From 33892a2e318abe2435942478e2d914e3a77db89b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 20 Aug 2020 17:50:30 +0200 Subject: [PATCH] Nav re-imagined (#3422) * Playthrough with top navigation * Sycky, but navigation works... * Adjust * grouping * It works, but is not great * Cleanups * Cleanups * hover color * Highlights * Highlights, part 2 * Background to body * Small adjustments (size, items) * Localize Modal/route switching * Remove light/full mode * Flatter single-entry menus * Adjustments for single-enrties (e.g. Kulupu) * Display NodeInfo * Misc * Content & Tabs sizing * Tab counters as bubbles * Alignment * Tabs styling * Adjust font color to defaults * Small tabs menu color adjustments * ... the previous, again, adjusted * Display Wiki/Github links * ar motions * Adjust Endpoints location * Ensure content grows to available height * Adjust tabs * Menu background adjustment * Adjust popup colors * Adjust highlights * Sligh breathing room * Adaptive header background * 2/3 * Adaptive is ok... for now. * Back to black... erm, gray * Lighten * Touchups. Stop playin, it is ok... * Sides readability * Cleanups. * Adjust. * Overall adjustments (theme). Fix Tag yellow. * Ok. Done. * Ship it. --- packages/apps-routing/src/accounts.ts | 1 + packages/apps-routing/src/claims.ts | 1 + packages/apps-routing/src/contracts.ts | 1 + packages/apps-routing/src/council.ts | 1 + packages/apps-routing/src/dashboard.ts | 1 + packages/apps-routing/src/democracy.ts | 1 + packages/apps-routing/src/explorer.ts | 1 + packages/apps-routing/src/extrinsics.ts | 1 + packages/apps-routing/src/generic-asset.ts | 1 + packages/apps-routing/src/index.ts | 70 ++--- packages/apps-routing/src/js.ts | 1 + packages/apps-routing/src/parachains.ts | 1 + packages/apps-routing/src/poll.ts | 1 + packages/apps-routing/src/settings.ts | 1 + packages/apps-routing/src/society.ts | 1 + packages/apps-routing/src/staking.ts | 1 + packages/apps-routing/src/storage.ts | 1 + packages/apps-routing/src/sudo.ts | 1 + packages/apps-routing/src/techcomm.ts | 1 + packages/apps-routing/src/toolbox.ts | 1 + packages/apps-routing/src/transfer.ts | 1 + packages/apps-routing/src/treasury.ts | 1 + packages/apps-routing/src/types.ts | 5 +- .../apps/public/locales/ar/translation.json | 4 +- .../apps/public/locales/en/app-council.json | 4 +- .../apps/public/locales/en/translation.json | 4 +- .../apps/public/locales/es/translation.json | 4 +- .../apps/public/locales/ja/translation.json | 4 +- .../apps/public/locales/pt/translation.json | 4 +- .../apps/public/locales/ru/translation.json | 4 +- .../apps/public/locales/zh/translation.json | 4 +- packages/apps/src/Apps.tsx | 185 +---------- packages/apps/src/Content/index.tsx | 6 +- .../apps/src/{SideBar => Menu}/ChainInfo.tsx | 58 ++-- packages/apps/src/Menu/Grouping.tsx | 91 ++++++ packages/apps/src/Menu/Item.tsx | 80 +++++ .../apps/src/{SideBar => Menu}/NodeInfo.tsx | 11 +- packages/apps/src/Menu/index.tsx | 205 ++++++++++++ packages/apps/src/Menu/types.tsx | 24 ++ packages/apps/src/SideBar/Item.tsx | 135 -------- packages/apps/src/SideBar/index.tsx | 294 ------------------ packages/apps/src/constants.ts | 15 - packages/apps/src/overlays/Base.tsx | 1 - packages/page-council/src/index.tsx | 3 +- packages/page-settings/src/General.tsx | 16 +- packages/page-settings/src/index.tsx | 7 +- packages/page-staking/src/index.tsx | 3 +- packages/react-components/src/Badge.tsx | 14 +- .../react-components/src/FilterOverlay.tsx | 2 +- packages/react-components/src/HelpOverlay.tsx | 4 +- packages/react-components/src/Progress.tsx | 2 +- packages/react-components/src/Sidebar.tsx | 2 +- packages/react-components/src/Table/Body.tsx | 2 +- packages/react-components/src/Tabs/Tab.tsx | 21 +- packages/react-components/src/Tabs/index.tsx | 7 +- packages/react-components/src/Tabs/types.ts | 1 + packages/react-components/src/Tag.tsx | 6 +- packages/react-components/src/styles/index.ts | 3 +- packages/react-query/src/NodeVersion.tsx | 3 +- 59 files changed, 568 insertions(+), 761 deletions(-) rename packages/apps/src/{SideBar => Menu}/ChainInfo.tsx (62%) create mode 100644 packages/apps/src/Menu/Grouping.tsx create mode 100644 packages/apps/src/Menu/Item.tsx rename packages/apps/src/{SideBar => Menu}/NodeInfo.tsx (84%) create mode 100644 packages/apps/src/Menu/index.tsx create mode 100644 packages/apps/src/Menu/types.tsx delete mode 100644 packages/apps/src/SideBar/Item.tsx delete mode 100644 packages/apps/src/SideBar/index.tsx delete mode 100644 packages/apps/src/constants.ts diff --git a/packages/apps-routing/src/accounts.ts b/packages/apps-routing/src/accounts.ts index e31c435485..8b2f135378 100644 --- a/packages/apps-routing/src/accounts.ts +++ b/packages/apps-routing/src/accounts.ts @@ -12,6 +12,7 @@ export default function create (t: (key: string, text: string, opti display: { needsApi: [] }, + group: 'accounts', icon: 'users', name: 'accounts', text: t('nav.accounts', 'Accounts', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/claims.ts b/packages/apps-routing/src/claims.ts index 510a87d809..c1ac3f480a 100644 --- a/packages/apps-routing/src/claims.ts +++ b/packages/apps-routing/src/claims.ts @@ -15,6 +15,7 @@ export default function create (t: (key: string, text: string, opti 'query.claims.claims' ] }, + group: 'accounts', icon: 'star', name: 'claims', text: t('nav.claims', 'Claim Tokens', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/contracts.ts b/packages/apps-routing/src/contracts.ts index a3167e97d3..504f7b2c7b 100644 --- a/packages/apps-routing/src/contracts.ts +++ b/packages/apps-routing/src/contracts.ts @@ -15,6 +15,7 @@ export default function create (t: (key: string, text: string, opti 'tx.contracts.call' ] }, + group: 'developer', icon: 'compress', name: 'contracts', text: t('nav.contracts', 'Contracts', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/council.ts b/packages/apps-routing/src/council.ts index 2858736b9e..4a00089b2a 100644 --- a/packages/apps-routing/src/council.ts +++ b/packages/apps-routing/src/council.ts @@ -17,6 +17,7 @@ export default function create (t: (key: string, text: string, opti ] ] }, + group: 'governance', icon: 'building', name: 'council', text: t('nav.council', 'Council', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/dashboard.ts b/packages/apps-routing/src/dashboard.ts index 9550365cbb..a68bf7ff00 100644 --- a/packages/apps-routing/src/dashboard.ts +++ b/packages/apps-routing/src/dashboard.ts @@ -12,6 +12,7 @@ export default function create (t: (key: string, text: string, opti display: { isHidden: true }, + group: 'network', icon: 'th', name: 'dashboard', text: t('nav.dashboard', 'Dashboard', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/democracy.ts b/packages/apps-routing/src/democracy.ts index b90c0e08b4..7c10fb1cda 100644 --- a/packages/apps-routing/src/democracy.ts +++ b/packages/apps-routing/src/democracy.ts @@ -14,6 +14,7 @@ export default function create (t: (key: string, text: string, opti 'tx.democracy.notePreimage' ] }, + group: 'governance', icon: 'calendar-check', name: 'democracy', text: t('nav.democracy', 'Democracy', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/explorer.ts b/packages/apps-routing/src/explorer.ts index 19a7180303..e14231c218 100644 --- a/packages/apps-routing/src/explorer.ts +++ b/packages/apps-routing/src/explorer.ts @@ -12,6 +12,7 @@ export default function create (t: (key: string, text: string, opti display: { needsApi: [] }, + group: 'network', icon: 'braille', name: 'explorer', text: t('nav.explorer', 'Explorer', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/extrinsics.ts b/packages/apps-routing/src/extrinsics.ts index fa38a845dc..08c7b4e32e 100644 --- a/packages/apps-routing/src/extrinsics.ts +++ b/packages/apps-routing/src/extrinsics.ts @@ -13,6 +13,7 @@ export default function create (t: (key: string, text: string, opti needsAccounts: true, needsApi: [] }, + group: 'developer', icon: 'sync', name: 'extrinsics', text: t('nav.extrinsics', 'Extrinsics', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/generic-asset.ts b/packages/apps-routing/src/generic-asset.ts index 3e9103de2f..dcf14752a3 100644 --- a/packages/apps-routing/src/generic-asset.ts +++ b/packages/apps-routing/src/generic-asset.ts @@ -16,6 +16,7 @@ export default function create (t: (key: string, text: string, opti 'tx.genericAsset.transfer' ] }, + group: 'network', icon: 'cubes', name: 'generic-asset', text: t('nav.generic-asset', 'Generic asset', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/index.ts b/packages/apps-routing/src/index.ts index cc5eab5943..abb04d3e0b 100644 --- a/packages/apps-routing/src/index.ts +++ b/packages/apps-routing/src/index.ts @@ -4,10 +4,6 @@ import { Routes } from './types'; -import appSettings from '@polkadot/ui-settings'; - -// When adding here, also ensure to add to Dummy.tsx - import accounts from './accounts'; import claims from './claims'; import contracts from './contracts'; @@ -31,47 +27,27 @@ import transfer from './transfer'; import treasury from './treasury'; export default function create (t: (key: string, text: string, options: { ns: string }) => T): Routes { - return appSettings.uiMode === 'light' - ? [ - // dashboard, - explorer(t), - accounts(t), - claims(t), - poll(t), - transfer(t), - genericAsset(t), - null, - staking(t), - democracy(t), - council(t), - // TODO Not sure about the inclusion of treasury, parachains & society here - null, - settings(t) - ] - : [ - // dashboard(t), - explorer(t), - accounts(t), - claims(t), - poll(t), - transfer(t), - genericAsset(t), - null, - staking(t), - democracy(t), - council(t), - treasury(t), - techcomm(t), - parachains(t), - society(t), - null, - contracts(t), - storage(t), - extrinsics(t), - sudo(t), - null, - settings(t), - toolbox(t), - js(t) - ]; + return [ + // dashboard(t), + accounts(t), + explorer(t), + claims(t), + poll(t), + transfer(t), + genericAsset(t), + staking(t), + democracy(t), + council(t), + treasury(t), + techcomm(t), + parachains(t), + society(t), + contracts(t), + storage(t), + extrinsics(t), + sudo(t), + settings(t), + toolbox(t), + js(t) + ]; } diff --git a/packages/apps-routing/src/js.ts b/packages/apps-routing/src/js.ts index 116f684788..0ed7d4c527 100644 --- a/packages/apps-routing/src/js.ts +++ b/packages/apps-routing/src/js.ts @@ -12,6 +12,7 @@ export default function create (t: (key: string, text: string, opti display: { needsApi: [] }, + group: 'developer', icon: 'code', name: 'js', text: t('nav.js', 'Javascript', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/parachains.ts b/packages/apps-routing/src/parachains.ts index e6e34fe41d..a35e5fcfe6 100644 --- a/packages/apps-routing/src/parachains.ts +++ b/packages/apps-routing/src/parachains.ts @@ -14,6 +14,7 @@ export default function create (t: (key: string, text: string, opti ['query.parachains.code', 'query.parachainUpgrade.didUpdateVFPs'] ] }, + group: 'network', icon: 'link', name: 'parachains', text: t('nav.parachains', 'Parachains', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/poll.ts b/packages/apps-routing/src/poll.ts index 8be49bcd9c..9575b83133 100644 --- a/packages/apps-routing/src/poll.ts +++ b/packages/apps-routing/src/poll.ts @@ -15,6 +15,7 @@ export default function create (t: (key: string, text: string, opti 'tx.poll.vote' ] }, + group: 'governance', icon: 'podcast', name: 'poll', text: t('nav.poll', 'Token poll', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/settings.ts b/packages/apps-routing/src/settings.ts index 9a56ba9225..cc31362c46 100644 --- a/packages/apps-routing/src/settings.ts +++ b/packages/apps-routing/src/settings.ts @@ -10,6 +10,7 @@ export default function create (t: (key: string, text: string, opti return { Component, display: {}, + group: 'settings', icon: 'cogs', name: 'settings', text: t('nav.settings', 'Settings', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/society.ts b/packages/apps-routing/src/society.ts index d9dfce775d..4022f57edf 100644 --- a/packages/apps-routing/src/society.ts +++ b/packages/apps-routing/src/society.ts @@ -15,6 +15,7 @@ export default function create (t: (key: string, text: string, opti 'query.society.pot' ] }, + group: 'network', icon: 'hand-spock', name: 'society', text: t('nav.society', 'Society', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/staking.ts b/packages/apps-routing/src/staking.ts index 0b21e50c6a..a136e2b419 100644 --- a/packages/apps-routing/src/staking.ts +++ b/packages/apps-routing/src/staking.ts @@ -14,6 +14,7 @@ export default function create (t: (key: string, text: string, opti ['tx.staking.bond'] ] }, + group: 'network', icon: 'certificate', name: 'staking', text: t('nav.staking', 'Staking', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/storage.ts b/packages/apps-routing/src/storage.ts index 89bc24666e..0d3a9df984 100644 --- a/packages/apps-routing/src/storage.ts +++ b/packages/apps-routing/src/storage.ts @@ -12,6 +12,7 @@ export default function create (t: (key: string, text: string, opti display: { needsApi: [] }, + group: 'developer', icon: 'database', name: 'chainstate', text: t('nav.storage', 'Chain state', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/sudo.ts b/packages/apps-routing/src/sudo.ts index 7918fb542e..65f1df4899 100644 --- a/packages/apps-routing/src/sudo.ts +++ b/packages/apps-routing/src/sudo.ts @@ -16,6 +16,7 @@ export default function create (t: (key: string, text: string, opti ], needsSudo: true }, + group: 'developer', icon: 'unlock', name: 'sudo', text: t('nav.sudo', 'Sudo', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/techcomm.ts b/packages/apps-routing/src/techcomm.ts index 50798b6376..6b42e077ea 100644 --- a/packages/apps-routing/src/techcomm.ts +++ b/packages/apps-routing/src/techcomm.ts @@ -15,6 +15,7 @@ export default function create (t: (key: string, text: string, opti 'query.technicalCommittee.members' ] }, + group: 'governance', icon: 'microchip', name: 'techcomm', text: t('nav.tech-comm', 'Tech. comm.', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/toolbox.ts b/packages/apps-routing/src/toolbox.ts index 436ecae94d..8d1d50d1f3 100644 --- a/packages/apps-routing/src/toolbox.ts +++ b/packages/apps-routing/src/toolbox.ts @@ -12,6 +12,7 @@ export default function create (t: (key: string, text: string, opti display: { needsApi: [] }, + group: 'developer', icon: 'tools', name: 'toolbox', text: t('nav.toolbox', 'Toolbox', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/transfer.ts b/packages/apps-routing/src/transfer.ts index de46f28c29..8384a8b282 100644 --- a/packages/apps-routing/src/transfer.ts +++ b/packages/apps-routing/src/transfer.ts @@ -17,6 +17,7 @@ export default function create (t: (key: string, text: string, opti 'tx.balances.transfer' ] }, + group: 'accounts', icon: 'paper-plane', name: 'transfer', text: t('nav.transfer', 'Transfer', { ns: 'apps-routing' }) diff --git a/packages/apps-routing/src/treasury.ts b/packages/apps-routing/src/treasury.ts index 93cddac603..d07338a28f 100644 --- a/packages/apps-routing/src/treasury.ts +++ b/packages/apps-routing/src/treasury.ts @@ -14,6 +14,7 @@ export default function create (t: (key: string, text: string, opti 'tx.treasury.proposeSpend' ] }, + group: 'governance', icon: 'gem', name: 'treasury', text: t('nav.treasury', 'Treasury', { ns: 'apps-routing' }), diff --git a/packages/apps-routing/src/types.ts b/packages/apps-routing/src/types.ts index 6dce432d8c..29e40bd015 100644 --- a/packages/apps-routing/src/types.ts +++ b/packages/apps-routing/src/types.ts @@ -5,6 +5,8 @@ import { IconName } from '@fortawesome/fontawesome-svg-core'; import { AppProps, BareProps } from '@polkadot/react-components/types'; +export type RouteGroup = 'accounts' | 'developer' | 'governance' | 'network' | 'settings'; + export interface RouteProps extends AppProps, BareProps { location: any; } @@ -19,6 +21,7 @@ export interface Route { needsApi?: (string | string[])[]; needsSudo?: boolean; }; + group: RouteGroup; icon: IconName; isIgnored?: boolean; name: string; @@ -26,4 +29,4 @@ export interface Route { useCounter?: () => number | string | null; } -export type Routes = (Route | null)[]; +export type Routes = Route[]; diff --git a/packages/apps/public/locales/ar/translation.json b/packages/apps/public/locales/ar/translation.json index f399911eaf..292f278878 100644 --- a/packages/apps/public/locales/ar/translation.json +++ b/packages/apps/public/locales/ar/translation.json @@ -248,7 +248,7 @@ "Mnemonic": "ذاكري", "Most profitable": "الأكثر ربحية", "Most recent head data": "أحدث بيانات الرأس", - "Motions ({{count}})": "({{الحركات ({{احسب", + "Motions": "الحركات", "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "(قد تنشأ مقترحات متعددة من جانب المجلس، سواء بالموافقة أو الرفض. طبق تصويتك على اقتراح المجلس الصحيح (متاح أيضا في صفحة طلبات المجلس", "Multisig": "متعدد الدخول", "Multisig approval with hash (non-final approval)": "(اعتماد متعدد التوقيعات مع تجزئة (موافقة غير نهائية", @@ -1277,4 +1277,4 @@ "{{threshold}}, not passing": "سقف}}، لا يتم تمريره}}", "{{threshold}}, passing": "سقف}}، يتم تمريره}}", "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "({{قفل}} (تاريخ إصدار {{أيام}}x رصيد التصويت، مقفل لـ x {{قيمة }}" -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/en/app-council.json b/packages/apps/public/locales/en/app-council.json index 71e18ab628..d3bb003086 100644 --- a/packages/apps/public/locales/en/app-council.json +++ b/packages/apps/public/locales/en/app-council.json @@ -6,7 +6,7 @@ "Council overview": "Council overview", "Current prime member, default voting": "Current prime member, default voting", "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.", - "Motions ({{count}})": "Motions ({{count}})", + "Motions": "Motions", "Nay {{counter}}": "Nay {{counter}}", "No candidates found": "No candidates found", "No council motions": "No council motions", @@ -77,4 +77,4 @@ "voting account": "voting account", "voting end": "voting end", "voting round": "voting round" -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/en/translation.json b/packages/apps/public/locales/en/translation.json index c654cca965..015396c32a 100644 --- a/packages/apps/public/locales/en/translation.json +++ b/packages/apps/public/locales/en/translation.json @@ -221,7 +221,7 @@ "Metadata {{count}}": "", "Mnemonic": "", "Most recent head data": "", - "Motions ({{count}})": "", + "Motions": "", "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "", "Multisig": "", "Multisig approval with hash (non-final approval)": "", @@ -1152,4 +1152,4 @@ "{{threshold}}, not passing": "", "{{threshold}}, passing": "", "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "" -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/es/translation.json b/packages/apps/public/locales/es/translation.json index cdf4500676..26ede30e79 100644 --- a/packages/apps/public/locales/es/translation.json +++ b/packages/apps/public/locales/es/translation.json @@ -205,7 +205,7 @@ "Metadata {{count}}": "Metadatos {{count}}", "Mnemonic": "Mnemonic", "Most recent head data": "Datos más recientes de la cabecera", - "Motions ({{count}})": "Mociones ({{count}})", + "Motions": "Mociones", "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Podrían existir múltiples propuestas del consejo, tanto de aprobación como de rechazo. Aplique su voto a la propuesta correcta del consejo (también disponible en la página de mociones del consejo)", "Multisig": "Multisig", "Multisig approval with hash (non-final approval)": "Aprobación de Multisig con hash (aprobación no definitiva)", @@ -1113,4 +1113,4 @@ "{{threshold}}, not passing": "{{threshold}}, no pasará", "{{threshold}}, passing": "{{threshold}}, de aprobación", "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x saldo de votos, bloqueado por {{lock}}x sentencia ({{period}} días." -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/ja/translation.json b/packages/apps/public/locales/ja/translation.json index 8a121f92b1..93c0653fa1 100644 --- a/packages/apps/public/locales/ja/translation.json +++ b/packages/apps/public/locales/ja/translation.json @@ -140,7 +140,7 @@ "Metadata {{count}}": "メタデータ {{count}}", "Mnemonic": "ニーモニック", "Most recent head data": "最新のヘッドデータ", - "Motions ({{count}})": "モーション({{count}})", + "Motions": "モーション", "Multisig": "マルチシグ", "Multisig approvals": "マルチシグの承認", "Mutates contract state": "コントラクトの状態を変える", @@ -562,4 +562,4 @@ "{{relayDispatchQueueSize}} dispatch messages pending": "{{relayDispatchQueueSize}} ディスパッチメッセージペンディング", "{{step}}. Sign with your ETH address": "{{step}}. ETHアドレスでサインしてください", "{{s}} s": "{{s}} 秒" -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/pt/translation.json b/packages/apps/public/locales/pt/translation.json index 93d6a760cf..9223f4764e 100644 --- a/packages/apps/public/locales/pt/translation.json +++ b/packages/apps/public/locales/pt/translation.json @@ -205,7 +205,7 @@ "Metadata {{count}}": "Metadados {{count}}", "Mnemonic": "Mnemônico", "Most recent head data": "Dados de cabeçalho mais recentes", - "Motions ({{count}})": "Moções ({{count}})", + "Motions": "Moções", "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Múltiplas propostas de conselho podem existir, tanto aprovação quanto rejeição. Aplique seu voto à proposta correta do conselho (também disponível na página de moções do conselho)", "Multisig": "Multi-assinatura", "Multisig approval with hash (non-final approval)": "Aprovação multi-assinatura com hash (aprovação não final)", @@ -1120,4 +1120,4 @@ "{{threshold}}, not passing": "{{threshold}}, não passando", "{{threshold}}, passing": "{{threshold}}, passando", "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}} x saldo de votos, bloqueado por {{lock}} x promulgação ({{period}} dias)" -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/ru/translation.json b/packages/apps/public/locales/ru/translation.json index 76a249471d..56fa746890 100644 --- a/packages/apps/public/locales/ru/translation.json +++ b/packages/apps/public/locales/ru/translation.json @@ -210,7 +210,7 @@ "Mnemonic": "Мнемонический", "More": "Ещё", "Most recent head data": "Последние данные", - "Motions ({{count}})": "Ходатайства ({{count}})", + "Motions": "Ходатайства", "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Нескольо предложний могут существовать, как одобренных так и отклоненных. Голосуйте в правильном предложении (это можно сделать и на странице Решения Совета)", "Multisig": "Мультиподпись", "Multisig approval with hash (non-final approval)": "Разрешить с хэшом мультиподписи (не финальное разрешение)", @@ -1131,4 +1131,4 @@ "{{threshold}}, not passing": "{{threshold}}, не проходит", "{{threshold}}, passing": "{{threshold}}, проходит", "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x баланс голосования, заблокировано на {{lock}}x ({{period}} дней)" -} \ No newline at end of file +} diff --git a/packages/apps/public/locales/zh/translation.json b/packages/apps/public/locales/zh/translation.json index 8e9ac50680..44e260b97c 100644 --- a/packages/apps/public/locales/zh/translation.json +++ b/packages/apps/public/locales/zh/translation.json @@ -123,7 +123,7 @@ "Manage your connection to Ledger S": "管理你的连接到 Ledger S", "Messages": "消息", "Mnemonic": "助记词", - "Motions ({{count}})": "Motions ({{count}})", + "Motions": "Motions", "Multisig": "多重签名", "Multisig approvals": "多重签名批准", "My accounts": "我的账户", @@ -561,4 +561,4 @@ "yes": "是", "your current password": "你当前的密码", "your new password": "你的新密码" -} \ No newline at end of file +} diff --git a/packages/apps/src/Apps.tsx b/packages/apps/src/Apps.tsx index 9ad6d32c30..ed02e6171f 100644 --- a/packages/apps/src/Apps.tsx +++ b/packages/apps/src/Apps.tsx @@ -4,8 +4,7 @@ import { BareProps as Props } from '@polkadot/react-components/types'; -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import store from 'store'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import AccountSidebar from '@polkadot/app-accounts/Sidebar'; import { getSystemChainColor } from '@polkadot/apps-config/ui'; @@ -15,86 +14,26 @@ import { useApi } from '@polkadot/react-hooks'; import Signer from '@polkadot/react-signer'; import ConnectingOverlay from './overlays/Connecting'; -import { SideBarTransition, SIDEBAR_MENU_THRESHOLD } from './constants'; import Content from './Content'; -import SideBar from './SideBar'; +import Menu from './Menu'; import WarmUp from './WarmUp'; -import { WindowDimensionsCtx } from './WindowDimensions'; - -interface SidebarState { - isCollapsed: boolean; - isMenu: boolean; - isMenuOpen: boolean; - transition: SideBarTransition; -} export const PORTAL_ID = 'portals'; -function saveSidebar (sidebar: SidebarState): SidebarState { - return store.set('sidebar', sidebar) as SidebarState; -} - function Apps ({ className = '' }: Props): React.ReactElement { - const windowDimensions = useContext(WindowDimensionsCtx); const { systemChain, systemName } = useApi(); - const [sidebar, setSidebar] = useState({ - isCollapsed: false, - isMenuOpen: false, - transition: SideBarTransition.COLLAPSED, - ...store.get('sidebar', {}), - isMenu: window.innerWidth < SIDEBAR_MENU_THRESHOLD - }); + const uiHighlight = useMemo( () => getSystemChainColor(systemChain, systemName), [systemChain, systemName] ); - const _collapse = useCallback( - () => setSidebar((sidebar: SidebarState) => saveSidebar({ ...sidebar, isCollapsed: !sidebar.isCollapsed })), - [] - ); - const _toggleMenu = useCallback( - () => setSidebar((sidebar: SidebarState) => saveSidebar({ ...sidebar, isCollapsed: false, isMenuOpen: true })), - [] - ); - const _handleResize = useCallback( - (): void => { - const transition = window.innerWidth < SIDEBAR_MENU_THRESHOLD - ? SideBarTransition.MINIMISED_AND_EXPANDED - : SideBarTransition.EXPANDED_AND_MAXIMISED; - - setSidebar((sidebar: SidebarState) => saveSidebar({ - ...sidebar, - isMenu: transition === SideBarTransition.MINIMISED_AND_EXPANDED, - isMenuOpen: false, - transition - })); - }, - [] - ); - - useEffect((): void => { - _handleResize(); - }, [_handleResize, windowDimensions]); - - const { isCollapsed, isMenu, isMenuOpen } = sidebar; - return ( <> -
+
+ -
- @@ -110,116 +49,6 @@ function Apps ({ className = '' }: Props): React.ReactElement { export default React.memo(styled(Apps)` box-sizing: border-box; display: flex; - flex-direction: row; - height: 100vh; - - &.theme--default { - a.apps--SideBar-Item-NavLink { - color: #f5f4f3; - display: block; - padding: 0.75em 0.75em; - white-space: nowrap; - - &:hover { - background: #5f5f5f; - border-radius: 0.28571429rem 0 0 0.28571429rem; - color: #eee; - margin-right: 0.25rem; - } - } - - a.apps--SideBar-Item-NavLink-active { - background: #f5f4f3; - border-radius: 0.28571429rem 0 0 0.28571429rem; - /* border-bottom: 2px solid transparent; */ - color: #3f3f3f; - - &:hover { - background: #f5f4f3; - color: #3f3f3f; - margin-right: 0; - } - } - } - - &.collapsed .apps--SideBar { - text-align: center; - - .divider { - display: none; - } - - .apps--SideBar-Item { - margin-left: 5px; - - .text { - display: none; - } - } - - .apps--SideBar-logo { - .apps--SideBar-logo-inner { - margin: auto; - padding: 0; - width: 3rem; - - img { - margin: 0 0.4rem; - } - - > div.info { - display: none; - } - } - } - - .apps--SideBar-collapse .ui.basic.secondary.button { - left: 0.66rem; - } - } - - &.expanded .apps--SideBar { - text-align: left; - - .apps--SideBar-Scroll { - padding-left: 0.75rem; - } - } - - &.fixed { - .apps--SideBar-Wrapper { - position: absolute; - width: 0px; - - .apps--SideBar { - padding-left: 0; - } - } - } - - &.menu-open { - .apps--SideBar-Wrapper { - width: 12rem; - } - } - - .apps--Menu-bg { - background: transparent; - height: 100%; - left: 0; - position: absolute; - top: 0; - transition: opacity 0.2s; - width: 100%; - z-index: 299; - - &.closed { - opacity: 0; - width: 0; - } - - &.open { - opacity: 1; - } - } + flex-direction: column; + min-height: 100vh; `); diff --git a/packages/apps/src/Content/index.tsx b/packages/apps/src/Content/index.tsx index 66ea117e5d..4804e2910e 100644 --- a/packages/apps/src/Content/index.tsx +++ b/packages/apps/src/Content/index.tsx @@ -25,6 +25,7 @@ const NOT_FOUND: Route = { display: { needsApi: undefined }, + group: 'settings', icon: 'times', isIgnored: false, name: 'unknown', @@ -78,13 +79,10 @@ function Content ({ className }: Props): React.ReactElement { } export default React.memo(styled(Content)` - background: #f5f4f3; flex-grow: 1; - height: 100%; - min-height: 100vh; overflow-x: hidden; overflow-y: auto; - padding: 0 1.5rem; + padding: 0 2.5rem 1rem; position: relative; width: 100%; diff --git a/packages/apps/src/SideBar/ChainInfo.tsx b/packages/apps/src/Menu/ChainInfo.tsx similarity index 62% rename from packages/apps/src/SideBar/ChainInfo.tsx rename to packages/apps/src/Menu/ChainInfo.tsx index a967896379..083248da0f 100644 --- a/packages/apps/src/SideBar/ChainInfo.tsx +++ b/packages/apps/src/Menu/ChainInfo.tsx @@ -7,73 +7,80 @@ import { RuntimeVersion } from '@polkadot/types/interfaces'; import React from 'react'; import styled from 'styled-components'; import { ChainImg, Icon } from '@polkadot/react-components'; -import { useCall, useApi } from '@polkadot/react-hooks'; +import { useApi, useCall, useIpfs, useToggle } from '@polkadot/react-hooks'; import { BestNumber, Chain } from '@polkadot/react-query'; +import Endpoints from '../Endpoints'; import { useTranslation } from '../translate'; interface Props { className?: string; - isToggled?: boolean; - onClick?: () => void; } -function ChainInfo ({ className = '', isToggled, onClick }: Props): React.ReactElement { +function ChainInfo ({ className }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const runtimeVersion = useCall(api.rpc.state.subscribeRuntimeVersion); + const { ipnsChain } = useIpfs(); + const [isEndpointsVisible, toggleEndpoints] = useToggle(); + const canToggle = !ipnsChain; return ( -
-
+
+
{runtimeVersion && (
{t('version {{version}}', { replace: { version: runtimeVersion.specVersion.toNumber() } })}
)} - +
- {onClick && ( + {canToggle && ( )}
+ {isEndpointsVisible && ( + + )}
); } export default React.memo(styled(ChainInfo)` - border-top: 0.5rem solid transparent; box-sizing: border-box; - padding: 0.75rem; - margin: 0 0 0.5rem -1rem; - - &.isClickable { - cursor: pointer; - } + padding: 0.75rem 1rem 0.75rem 1.5rem; + margin: 0; .apps--SideBar-logo-inner { display: flex; align-items: center; justify-content: space-between; - width: 10.5rem; + + &.isClickable { + cursor: pointer; + } img { flex: 0; - height: 2.75rem; - width: 2.75rem; + height: 3rem; + margin-right: 0.5rem; + width: 3rem; } .ui--Icon.dropdown, > div.info { color: white; - opacity: 0.75; + opacity: 0.85; text-align: right; vertical-align: middle; } @@ -88,14 +95,15 @@ export default React.memo(styled(ChainInfo)` flex: 1; padding-right: 0.5rem; + > div.bestNumber, > div.chain { font-size: 0.9rem; - line-height: 1rem; + line-height: 1.2; } > div.runtimeVersion { font-size: 0.75rem; - line-height: 1rem; + line-height: 1.2; } } } diff --git a/packages/apps/src/Menu/Grouping.tsx b/packages/apps/src/Menu/Grouping.tsx new file mode 100644 index 0000000000..cae6107c8f --- /dev/null +++ b/packages/apps/src/Menu/Grouping.tsx @@ -0,0 +1,91 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Group } from './types'; + +import React from 'react'; +import styled from 'styled-components'; +import { Icon } from '@polkadot/react-components'; + +import Item from './Item'; + +interface Props extends Group { + className?: string; +} + +function Grouping ({ className, name, routes }: Props): React.ReactElement { + if (routes.length === 1) { + return ( + + ); + } + + return ( +
  • +
    + {name} + +
    +
      + {routes.map((route): React.ReactNode => ( + + ))} +
    +
  • + ); +} + +export default React.memo(styled(Grouping)` + cursor: pointer; + position: relative; + + .groupHdr { + border-radius: 0.25rem 0.25rem 0 0; + padding: 1rem 1.5rem; + + > .ui--Icon { + margin-left: 0.75rem; + } + } + + .groupMenu { + background: #6a6662; + border-radius: 0 0 0.25rem 0.25rem; + display: none; + margin: 0; + overflow: hidden; + padding: 0; + position: absolute; + z-index: 250; + + > li { + z-index: 1; + + a { + padding-right: 3.5rem; + } + } + } + + &:hover { + .groupHdr, + .groupMenu li { + background: #74706c; + } + + .groupMenu { + display: block; + + > li:hover { + background: transparent; + } + } + } +`); diff --git a/packages/apps/src/Menu/Item.tsx b/packages/apps/src/Menu/Item.tsx new file mode 100644 index 0000000000..d9ac3b7513 --- /dev/null +++ b/packages/apps/src/Menu/Item.tsx @@ -0,0 +1,80 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ItemRoute } from './types'; + +import React from 'react'; +import styled from 'styled-components'; +import { Badge, Icon } from '@polkadot/react-components'; +import { useToggle } from '@polkadot/react-hooks'; + +interface Props { + className?: string; + route: ItemRoute; +} + +const DUMMY_COUNTER = () => 0; + +function Item ({ className = '', route: { Modal, href, icon, name, text, useCounter = DUMMY_COUNTER } }: Props): React.ReactElement { + const [isModalVisible, toggleModal] = useToggle(); + const count = useCounter(); + + return ( +
  • + + + {text} + {!!count && ( + + )} + + {Modal && isModalVisible && ( + + )} +
  • + ); +} + +export default React.memo(styled(Item)` + cursor: pointer; + position: relative; + white-space: nowrap; + + &.topLevel { + padding: 0; + + a { + padding: 1rem 1.5rem; + } + } + + &.withCounter a { + padding-right: 3.5rem; + } + + a { + color: inherit !important; + display: block; + padding: 0.75rem 1.5rem; + text-decoration: none; + } + + .ui--Badge { + position: absolute; + right: 0.25rem; + top: 0.65rem; + } + + .ui--Icon { + margin-right: 0.5rem; + } +`); diff --git a/packages/apps/src/SideBar/NodeInfo.tsx b/packages/apps/src/Menu/NodeInfo.tsx similarity index 84% rename from packages/apps/src/SideBar/NodeInfo.tsx rename to packages/apps/src/Menu/NodeInfo.tsx index 297093b3b8..25ba1c1bc3 100644 --- a/packages/apps/src/SideBar/NodeInfo.tsx +++ b/packages/apps/src/Menu/NodeInfo.tsx @@ -14,11 +14,11 @@ const pkgJson = require('../../package.json') as { version: string }; const uiInfo = `apps v${pkgJson.version}`; -function NodeInfo ({ className }: Props): React.ReactElement { +function NodeInfo ({ className = '' }: Props): React.ReactElement { const { api, isApiReady } = useApi(); return ( -
    +
    {isApiReady && (
      @@ -34,9 +34,10 @@ function NodeInfo ({ className }: Props): React.ReactElement { export default React.memo(styled(NodeInfo)` background: transparent; color: white; - font-size: 0.75rem; - opacity: 0.5; - padding: 0 1.5rem 0 1.5rem; + font-size: 0.9rem; + line-height: 1.2; + opacity: 0.85; + padding: 0 1.5rem; text-align: right; > div { diff --git a/packages/apps/src/Menu/index.tsx b/packages/apps/src/Menu/index.tsx new file mode 100644 index 0000000000..d899e46f33 --- /dev/null +++ b/packages/apps/src/Menu/index.tsx @@ -0,0 +1,205 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route, Routes } from '@polkadot/apps-routing/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { AccountId } from '@polkadot/types/interfaces'; +import { Group, Groups, ItemRoute } from './types'; + +import { TFunction } from 'i18next'; +import React, { useMemo, useRef } from 'react'; +import { useLocation } from 'react-router-dom'; +import styled from 'styled-components'; +import createRoutes from '@polkadot/apps-routing'; +import { Icon } from '@polkadot/react-components'; +import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; + +import { findMissingApis } from '../endpoint'; +import { useTranslation } from '../translate'; +import ChainInfo from './ChainInfo'; +import Grouping from './Grouping'; +import Item from './Item'; +import NodeInfo from './NodeInfo'; + +interface Props { + className?: string; +} + +const disabledLog = new Map(); + +function createExternals (t: TFunction): ItemRoute[] { + return [ + { href: 'https://github.com/polkadot-js/apps', icon: 'code-branch', name: 'github', text: t('nav.github', 'GitHub', { ns: 'apps-routing' }) }, + { href: 'https://wiki.polkadot.network', icon: 'book', name: 'wiki', text: t('nav.wiki', 'Wiki', { ns: 'apps-routing' }) } + ]; +} + +function logDisabled (route: string, message: string): void { + if (!disabledLog.get(route)) { + disabledLog.set(route, message); + + console.warn(`Disabling ${route}: ${message}`); + } +} + +function checkVisible (name: string, { api, isApiConnected, isApiReady }: ApiProps, hasAccounts: boolean, hasSudo: boolean, { isHidden, needsAccounts, needsApi, needsSudo }: Route['display']): boolean { + if (isHidden) { + return false; + } else if (needsAccounts && !hasAccounts) { + return false; + } else if (!needsApi) { + return true; + } else if (!isApiReady || !isApiConnected) { + return false; + } else if (needsSudo && !hasSudo) { + logDisabled(name, 'Sudo key not available'); + + return false; + } + + const notFound = findMissingApis(api, needsApi); + + if (notFound.length !== 0) { + logDisabled(name, `API not available: ${notFound.toString()}`); + } + + return notFound.length === 0; +} + +function extractGroups (routing: Routes, groupNames: Record, apiProps: ApiProps, hasAccounts: boolean, hasSudo: boolean): Group[] { + return Object + .values( + routing.reduce((all: Groups, route): Groups => { + if (!all[route.group]) { + all[route.group] = { name: groupNames[route.group], routes: [route] }; + } else { + all[route.group].routes.push(route); + } + + return all; + }, {}) + ) + .map(({ name, routes }): Group => ({ + name, + routes: routes.filter(({ display, name }) => checkVisible(name, apiProps, hasAccounts, hasSudo, display)) + })) + .filter(({ routes }) => routes.length); +} + +function Menu ({ className = '' }: Props): React.ReactElement { + const { t } = useTranslation(); + const { allAccounts, hasAccounts } = useAccounts(); + const apiProps = useApi(); + const sudoKey = useCall(apiProps.isApiReady && apiProps.api.query.sudo?.key); + const location = useLocation(); + + const externalRef = useRef(createExternals(t)); + + const groupRef = useRef({ + accounts: t('Accounts'), + developer: t('Developer'), + governance: t('Governance'), + network: t('Network'), + settings: t('Settings') + }); + + const routeRef = useRef(createRoutes(t)); + + const hasSudo = useMemo( + () => !!sudoKey && allAccounts.some((address) => sudoKey.eq(address)), + [allAccounts, sudoKey] + ); + + const visibleGroups = useMemo( + () => extractGroups(routeRef.current, groupRef.current, apiProps, hasAccounts, hasSudo), + [apiProps, hasAccounts, hasSudo] + ); + + const activeRoute = useMemo( + () => routeRef.current.find((route) => location.pathname.startsWith(`/${route.name}`)) || null, + [location] + ); + + const isLoading = !apiProps.isApiReady || !apiProps.isApiConnected; + + return ( +
    +
    + + {activeRoute && ( +
    + + {activeRoute.text} +
    + )} +
      + {visibleGroups.map(({ name, routes }): React.ReactNode => ( + + ))} +
    +
    +
    +
      + {externalRef.current.map((route): React.ReactNode => ( + + ))} +
    +
    + +
    + ); +} + +export default React.memo(styled(Menu)` + align-items: center; + background: #6a6662; + border-top: 0.5rem solid transparent; + display: flex; + justify-content: space-between; + padding: 0; + position: relative; + z-index: 220; + + .menuSection { + align-items: flex-end; + align-self: flex-end; + display: flex; + } + + .menuActive { + background: #fefcfa; + border-radius: 0.25rem 0.25rem 0 0; + padding: 1rem 1.5rem; + margin: 0 1.5rem; + + &.isLoading { + background: #f5f3f1; + } + + .ui--Icon { + margin-right: 0.5rem; + } + } + + .menuItems { + background: #6a6662; + color: #f5f4f3; + flex: 1 1; + list-style: none; + margin: 0 2rem 0 0; + padding: 0; + + > li { + display: inline-block; + } + } +`); diff --git a/packages/apps/src/Menu/types.tsx b/packages/apps/src/Menu/types.tsx new file mode 100644 index 0000000000..a414b49b71 --- /dev/null +++ b/packages/apps/src/Menu/types.tsx @@ -0,0 +1,24 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { IconName } from '@fortawesome/fontawesome-svg-core'; +import { Routes } from '@polkadot/apps-routing/types'; + +import React from 'react'; + +export interface ItemRoute { + Modal?: React.ComponentType; + href?: string; + icon: IconName; + name: string; + text: string; + useCounter?: () => number | string | null; +} + +export interface Group { + name: string; + routes: Routes; +} + +export type Groups = Record; diff --git a/packages/apps/src/SideBar/Item.tsx b/packages/apps/src/SideBar/Item.tsx deleted file mode 100644 index 3b6eefd3f3..0000000000 --- a/packages/apps/src/SideBar/Item.tsx +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2017-2020 @polkadot/apps authors & contributors -// This software may be modified and distributed under the terms -// of the Apache-2.0 license. See the LICENSE file for details. - -import { ApiProps } from '@polkadot/react-api/types'; -import { Route } from '@polkadot/apps-routing/types'; -import { AccountId } from '@polkadot/types/interfaces'; - -import React, { useEffect, useState } from 'react'; -import { NavLink } from 'react-router-dom'; -import { Badge, Icon, Menu, Tooltip } from '@polkadot/react-components'; -import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; - -import { findMissingApis } from '../endpoint'; - -const DUMMY_COUNTER = (): null => null; - -interface Props { - isCollapsed: boolean; - onClick: () => void; - route: Route; -} - -const disabledLog = new Map(); -const TOOLTIP_OFFSET = { right: -4 }; - -function logDisabled (route: string, message: string): void { - if (!disabledLog.get(route)) { - disabledLog.set(route, message); - - console.warn(`Disabling ${route}: ${message}`); - } -} - -function checkVisible (name: string, { api, isApiConnected, isApiReady }: ApiProps, hasAccounts: boolean, hasSudo: boolean, { isHidden, needsAccounts, needsApi, needsSudo }: Route['display']): boolean { - if (isHidden) { - return false; - } else if (needsAccounts && !hasAccounts) { - return false; - } else if (!needsApi) { - return true; - } else if (!isApiReady || !isApiConnected) { - return false; - } else if (needsSudo && !hasSudo) { - logDisabled(name, 'Sudo key not available'); - - return false; - } - - const notFound = findMissingApis(api, needsApi); - - if (notFound.length !== 0) { - logDisabled(name, `API not available: ${notFound.toString()}`); - } - - return notFound.length === 0; -} - -function Item ({ isCollapsed, onClick, route }: Props): React.ReactElement | null { - const { allAccounts, hasAccounts } = useAccounts(); - const apiProps = useApi(); - const sudoKey = useCall(apiProps.isApiReady && apiProps.api.query.sudo?.key); - const [hasSudo, setHasSudo] = useState(false); - const [isVisible, setIsVisible] = useState(false); - const count = (route.useCounter || DUMMY_COUNTER)(); - - useEffect((): void => { - setHasSudo(!!sudoKey && allAccounts.some((address) => sudoKey.eq(address))); - }, [allAccounts, sudoKey]); - - useEffect((): void => { - const isVisible = checkVisible(route.name, apiProps, hasAccounts, hasSudo, route.display); - - route.isIgnored = !isVisible; - setIsVisible(isVisible); - }, [apiProps, hasAccounts, hasSudo, route]); - - if (!isVisible) { - return null; - } - - const { Modal, icon, name, text } = route; - - const body = ( - <> - - {text} - {!!count && ( - - )} - - - ); - - return ( - - {Modal - ? ( - - {body} - - ) - : ( - - {body} - - ) - } - - ); -} - -export default React.memo(Item); diff --git a/packages/apps/src/SideBar/index.tsx b/packages/apps/src/SideBar/index.tsx deleted file mode 100644 index cfdc45efe1..0000000000 --- a/packages/apps/src/SideBar/index.tsx +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2017-2020 @polkadot/apps authors & contributors -// This software may be modified and distributed under the terms -// of the Apache-2.0 license. See the LICENSE file for details. - -import { Routes } from '@polkadot/apps-routing/types'; - -import React, { useCallback, useMemo, useState } from 'react'; -import styled from 'styled-components'; -import createRoutes from '@polkadot/apps-routing'; -import { Button, ChainImg, Icon, Menu, media } from '@polkadot/react-components'; -import { useIpfs } from '@polkadot/react-hooks'; - -import Endpoints from '../Endpoints'; -import { SIDEBAR_MENU_THRESHOLD } from '../constants'; -import { useTranslation } from '../translate'; -import ChainInfo from './ChainInfo'; -import Item from './Item'; -import NodeInfo from './NodeInfo'; - -interface Props { - className?: string; - collapse: () => void; - handleResize: () => void; - isCollapsed: boolean; - isMenuOpen: boolean; - toggleMenu: () => void; -} - -const SIDE_BORDER_WIDTH = '0.65rem'; -const MENU_WIDTH_COLLAPSED = '4.2rem'; -const MENU_WIDTH_EXPANDED = '12rem'; - -function AppsSidebar ({ className = '', collapse, handleResize, isCollapsed, isMenuOpen, toggleMenu }: Props): React.ReactElement { - const { t } = useTranslation(); - const { ipnsChain } = useIpfs(); - const [modals, setModals] = useState>( - createRoutes(t).reduce((result: Record, route): Record => { - if (route && route.Modal) { - result[route.name] = false; - } - - return result; - }, { network: false }) - ); - - const routing = useMemo( - () => createRoutes(t), - [t] - ); - - const _toggleModal = useCallback( - (name: string): () => void => - (): void => setModals((modals: Record) => ({ - ...modals, - [name]: !modals[name] - })), - [] - ); - - return ( -
    - - {modals.network && ( - - )} - {routing.map((route): React.ReactNode => ( - route?.Modal - ? route.Modal && modals[route.name] - ? ( - - ) - :
    - : null - ))} -
    - -
    - - {routing.map((route, index): React.ReactNode => ( - route - ? ( - - ) - : ( -
    -
    -
    -
    -
    -
    -
    - ); -} - -export default React.memo(styled(AppsSidebar)` - display: flex; - position: relative; - z-index: 300; - - &.collapsed { - width: ${MENU_WIDTH_COLLAPSED}; - } - - &.expanded { - width: ${MENU_WIDTH_EXPANDED}; - } - - .apps--SideBar { - align-items: center; - background: #4f5255; - box-sizing: border-box; - display: flex; - flex-flow: column; - height: auto; - position: relative; - transition: left 0.3s linear; - width: 100%; - - .apps--SideBar-border { - border-top: ${SIDE_BORDER_WIDTH} solid transparent; - position: absolute; - left: 0; - right: 0; - top: 0; - } - - .ui.vertical.menu { - display: flex; - height: 100vh; - margin: 0; - top: 0; - width: 100%; - position: sticky; - } - - .apps--SideBar-Scroll { - align-items: center; - display: flex; - flex-direction: column; - height: 100vh; - overflow-y: auto; - width: 100%; - scrollbar-width: none; - - &::-webkit-scrollbar { - display: none; - width: 0px; - } - } - - .apps--SideBar-Item { - align-self: flex-end; - flex-grow: 0; - padding: 0 !important; - position: relative; - width: inherit; - - .text { - padding-left: 0.5rem; - } - - .ui--Badge { - margin: 0; - position: absolute; - right: 0.5rem; - top: 0.55rem; - z-index: 1; - } - } - - .apps--SideBar-collapse { - background: #4f5255; - bottom: 0; - left: 0; - padding: 0.75rem 0 .75rem 0.65rem; - position: sticky; - right: 0; - text-align: left; - width: 100%; - } - - .apps--SideBar-toggle { - height: 100%; - position: absolute; - right: 0px; - top: 0px; - transition: all 0.2s; - width: 6px; - - &:hover { - background: rgba(255,255,255,0.15); - cursor: pointer; - } - } - } - - .toggleImg { - cursor: pointer; - height: 2.75rem; - left: 0.9rem; - opacity: 0; - position: absolute; - top: 0px; - transition: opacity 0.2s ease-in, top 0.2s ease-in; - width: 2.75rem; - - &.delayed { - transition-delay: 0.4s; - } - - &.open { - opacity: 1; - top: 0.9rem; - } - - ${media.DESKTOP` - opacity: 0 !important; - top: -2.9rem !important; - `} - } - - @media only screen and (max-width: ${SIDEBAR_MENU_THRESHOLD}px) { - .apps--SideBar-collapse, - .apps--Sidebar-toggle { - display: none; - } - } -`); diff --git a/packages/apps/src/constants.ts b/packages/apps/src/constants.ts deleted file mode 100644 index 0a938803ed..0000000000 --- a/packages/apps/src/constants.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017-2020 @polkadot/apps authors & contributors -// This software may be modified and distributed under the terms -// of the Apache-2.0 license. See the LICENSE file for details. - -import { ScreenSizes } from '@polkadot/react-components/constants'; - -export enum SideBarTransition { - COLLAPSED = 'COLLAPSED', - EXPANDED = 'EXPANDED', - EXPANDED_AND_MAXIMISED = 'EXPANDED_AND_MAXIMISED', - MINIMISED_AND_EXPANDED = 'MINIMISED_AND_EXPANDED' -} - -export const SIDEBAR_MENU_THRESHOLD = ScreenSizes.DESKTOP; -export const SIDEBAR_TRANSITION_DURATION = 300; diff --git a/packages/apps/src/overlays/Base.tsx b/packages/apps/src/overlays/Base.tsx index 136d5b33ab..1f14006d42 100644 --- a/packages/apps/src/overlays/Base.tsx +++ b/packages/apps/src/overlays/Base.tsx @@ -51,7 +51,6 @@ export default React.memo(styled(BaseOverlay)` border-bottom-left-radius: 0.25rem; border-bottom-width: 1px; border-left-width: 0.25rem; - right: 0; line-height: 1.5em; padding: 0 1rem; position: fixed; diff --git a/packages/page-council/src/index.tsx b/packages/page-council/src/index.tsx index fcf2caf49a..1dcba6672c 100644 --- a/packages/page-council/src/index.tsx +++ b/packages/page-council/src/index.tsx @@ -44,8 +44,9 @@ function CouncilApp ({ basePath, className }: Props): React.ReactElement text: t('Council overview') }, { + count: numMotions, name: 'motions', - text: t('Motions ({{count}})', { replace: { count: numMotions } }) + text: t('Motions') } ], [numMotions, t]); diff --git a/packages/page-settings/src/General.tsx b/packages/page-settings/src/General.tsx index eaf07a981f..8ffa2681ed 100644 --- a/packages/page-settings/src/General.tsx +++ b/packages/page-settings/src/General.tsx @@ -24,14 +24,17 @@ function General ({ className = '' }: Props): React.ReactElement { // tri-state: null = nothing changed, false = no reload, true = reload required const [changed, setChanged] = useState(null); const [settings, setSettings] = useState(uiSettings.get()); + const iconOptions = useMemo( () => uiSettings.availableIcons.map((o): Option => createIdenticon(o, ['default'])), [] ); + const prefixOptions = useMemo( () => createSs58(t).map((o): Option | React.ReactNode => createOption(o, ['default'])), [t] ); + const translateLanguages = useMemo( () => createLanguages(t), [t] @@ -54,10 +57,12 @@ function General ({ className = '' }: Props): React.ReactElement { setSettings((settings) => ({ ...settings, [key]: value })), [] ); + const _saveAndReload = useCallback( () => saveAndReload(settings), [settings] ); + const _save = useCallback( (): void => { save(settings); @@ -66,7 +71,7 @@ function General ({ className = '' }: Props): React.ReactElement { [settings] ); - const { i18nLang, icon, ledgerConn, prefix, uiMode } = settings; + const { i18nLang, icon, ledgerConn, prefix } = settings; return (
    @@ -88,15 +93,6 @@ function General ({ className = '' }: Props): React.ReactElement { options={iconOptions} />
    -
    - ('Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)')} - label={t('interface operation mode')} - onChange={_handleChange('uiMode')} - options={uiSettings.availableUIModes} - /> -
    {isLedgerCapable() && (
    ('General') }, { + count: numExtensions, name: 'metadata', - text: t('Metadata {{count}}', { - replace: { - count: numExtensions ? `(${numExtensions})` : '' - } - }) + text: t('Metadata') }, { name: 'developer', diff --git a/packages/page-staking/src/index.tsx b/packages/page-staking/src/index.tsx index c80a665199..d4c9d9b4f2 100644 --- a/packages/page-staking/src/index.tsx +++ b/packages/page-staking/src/index.tsx @@ -90,8 +90,9 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement('Waiting') }, { + count: slashes.reduce((count, [, unapplied]) => count + unapplied.length, 0), name: 'slashes', - text: t('Slashes ({{count}})', { replace: { count: slashes.reduce((count, [, unapplied]) => count + unapplied.length, 0) } }) + text: t('Slashes') }, { hasParams: true, diff --git a/packages/react-components/src/Badge.tsx b/packages/react-components/src/Badge.tsx index b8ef431603..baf1cec732 100644 --- a/packages/react-components/src/Badge.tsx +++ b/packages/react-components/src/Badge.tsx @@ -12,7 +12,7 @@ import Tooltip from './Tooltip'; interface Props { className?: string; - color: 'counter' | 'green' | 'blue' | 'gray' | 'normal' | 'purple' | 'red' | 'transparent'; + color: 'blue' | 'counter' | 'gray' | 'green' | 'highlight' | 'normal' | 'purple' | 'red' | 'transparent'; hover?: React.ReactNode; icon?: IconName; info?: React.ReactNode; @@ -20,6 +20,8 @@ interface Props { onClick?: () => void; } +const HIGHLIGHTS = ['counter', 'highlight']; + let badgeId = 0; function Badge ({ className = '', color = 'normal', hover, icon, info, isSmall, onClick }: Props): React.ReactElement | null { @@ -27,11 +29,12 @@ function Badge ({ className = '', color = 'normal', hover, icon, info, isSmall, const extraProps = hover ? { 'data-for': trigger, 'data-tip': true } : {}; + const isHighlight = HIGHLIGHTS.includes(color); return (
    {info || (icon && )} @@ -47,7 +50,7 @@ function Badge ({ className = '', color = 'normal', hover, icon, info, isSmall, export default React.memo(styled(Badge)` border-radius: 16px; - color: #eee; + color: #eeedec; display: inline-block; font-size: 12px; height: 22px; @@ -93,14 +96,13 @@ export default React.memo(styled(Badge)` } &.counterColor { - background: red; margin: 0 0.5rem; vertical-align: middle; } &.grayColor { - background: #eee !important; - color: #aaa; + background: #eeedec !important; + color: #aaa9a8; } &.redColor { diff --git a/packages/react-components/src/FilterOverlay.tsx b/packages/react-components/src/FilterOverlay.tsx index 157d92a802..c4f4c3e136 100644 --- a/packages/react-components/src/FilterOverlay.tsx +++ b/packages/react-components/src/FilterOverlay.tsx @@ -32,7 +32,7 @@ export default React.memo(styled(FilterOverlay)` justify-content: flex-end; position: absolute; right: 5rem; - top: 0.4rem; + top: 0.525rem; > div { max-width: 35rem !important; diff --git a/packages/react-components/src/HelpOverlay.tsx b/packages/react-components/src/HelpOverlay.tsx index 88454b904f..3d616c861a 100644 --- a/packages/react-components/src/HelpOverlay.tsx +++ b/packages/react-components/src/HelpOverlay.tsx @@ -46,13 +46,13 @@ export default React.memo(styled(HelpOverlay)` .help-button { cursor: pointer; font-size: 2rem; - padding: 1.25rem 1.5rem 0 0; + padding: 1rem 2.5rem 0 0; } > .help-button { position: absolute; right: 0rem; - top: 0rem; + top: 0.125rem; } .help-slideout { diff --git a/packages/react-components/src/Progress.tsx b/packages/react-components/src/Progress.tsx index c9bb23b128..75a664490d 100644 --- a/packages/react-components/src/Progress.tsx +++ b/packages/react-components/src/Progress.tsx @@ -139,7 +139,7 @@ export default React.memo(styled(Progress)` div { line-height: 1; font-size: 1.25rem; - text-shadow: 0 0 2px #f5f4f3; + text-shadow: 0 0 2px #f5f3f1; } } diff --git a/packages/react-components/src/Sidebar.tsx b/packages/react-components/src/Sidebar.tsx index ee78f671ec..1853967a50 100644 --- a/packages/react-components/src/Sidebar.tsx +++ b/packages/react-components/src/Sidebar.tsx @@ -34,7 +34,7 @@ function Sidebar ({ button, children, className = '', onClose }: Props): React.R } export default React.memo(styled(Sidebar)(({ offset = 0, position }: Props) => ` - background: #f5f4f3; + background: #f5f3f1; bottom: 0; box-shadow: ${position === 'right' ? '-6px' : '6px'} 0px 20px 0px rgba(0, 0, 0, 0.3); margin-left: -0.125rem; diff --git a/packages/react-components/src/Table/Body.tsx b/packages/react-components/src/Table/Body.tsx index 38f0ae04df..a8b215f081 100644 --- a/packages/react-components/src/Table/Body.tsx +++ b/packages/react-components/src/Table/Body.tsx @@ -137,7 +137,7 @@ export default React.memo(styled(Body)` tr { &:nth-child(even) { - background: #faf9f8; + background: #faf8f6; } &:nth-child(odd) { diff --git a/packages/react-components/src/Tabs/Tab.tsx b/packages/react-components/src/Tabs/Tab.tsx index 9613fb9e1a..8b6ce0e293 100644 --- a/packages/react-components/src/Tabs/Tab.tsx +++ b/packages/react-components/src/Tabs/Tab.tsx @@ -8,17 +8,19 @@ import React from 'react'; import { NavLink } from 'react-router-dom'; import styled from 'styled-components'; +import Badge from '../Badge'; import Icon from '../Icon'; interface Props extends TabItem { basePath: string; className?: string; + count?: number; index: number; isSequence?: boolean; num: number; } -function Tab ({ basePath, className = '', hasParams, index, isExact, isRoot, isSequence, name, num, text }: Props): React.ReactElement { +function Tab ({ basePath, className = '', count, hasParams, index, isExact, isRoot, isSequence, name, num, text }: Props): React.ReactElement { const to = isRoot ? basePath : `${basePath}/${name}`; @@ -41,19 +43,24 @@ function Tab ({ basePath, className = '', hasParams, index, isExact, isRoot, isS icon='arrow-right' /> )} + {!!count && ( + + )} ); } export default React.memo(styled(Tab)` border-bottom: 2px solid transparent; - color: rgba(0, 0, 0, 0.87) !important; - padding: 0.75rem 1.5rem; + color: #4e4e4e !important; + padding: 0.5rem 1.5rem 0.75rem; &.tabLinkActive { border-bottom-color: #e6e6e6; - color: rgba(0, 0, 0, 0.95); - font-weight: 700; } &:hover { @@ -64,6 +71,10 @@ export default React.memo(styled(Tab)` } } + .tabCounter { + margin: -1rem 0 -1rem 0.5rem; + } + .tabIcon { margin-left: 0.75rem; } diff --git a/packages/react-components/src/Tabs/index.tsx b/packages/react-components/src/Tabs/index.tsx index c53f4d8eb2..bfca61a38f 100644 --- a/packages/react-components/src/Tabs/index.tsx +++ b/packages/react-components/src/Tabs/index.tsx @@ -59,10 +59,13 @@ function Tabs ({ basePath, className = '', hidden, isSequence, items }: Props): } export default React.memo(styled(Tabs)` + align-items: flex-end; + background: #fefcfa; border-bottom: 1px solid #e6e6e6; - margin: 0 -2em; + display: flex; + margin: 0 -3rem; overflow-x: scroll; - padding: 2em 2em 0.75rem; + padding: 1.5rem 3rem 0; text-align: left; &::-webkit-scrollbar { diff --git a/packages/react-components/src/Tabs/types.ts b/packages/react-components/src/Tabs/types.ts index 63087b1ac5..457f0efc88 100644 --- a/packages/react-components/src/Tabs/types.ts +++ b/packages/react-components/src/Tabs/types.ts @@ -6,6 +6,7 @@ import React from 'react'; export interface TabItem { alias?: string; + count?: number; hasParams?: boolean; isExact?: boolean; isRoot?: boolean; diff --git a/packages/react-components/src/Tag.tsx b/packages/react-components/src/Tag.tsx index 6091028f2e..76dcf8462f 100644 --- a/packages/react-components/src/Tag.tsx +++ b/packages/react-components/src/Tag.tsx @@ -9,7 +9,7 @@ import Tooltip from './Tooltip'; interface Props { className?: string; - color?: 'blue' | 'green' | 'grey' | 'orange' | 'pink' | 'red'; + color?: 'blue' | 'green' | 'grey' | 'orange' | 'pink' | 'red' | 'yellow'; hover?: React.ReactNode; isTag?: boolean; label: React.ReactNode; @@ -82,6 +82,10 @@ export default React.memo(styled(Tag)` background: #db2828; } + &.yellowColor { + background: darkgoldenrod; + } + &.isTag { border-radius: 0 0.25rem 0.25rem 0; padding-left: 1.5em; diff --git a/packages/react-components/src/styles/index.ts b/packages/react-components/src/styles/index.ts index b2c70c7fa6..8a1a4ff46b 100644 --- a/packages/react-components/src/styles/index.ts +++ b/packages/react-components/src/styles/index.ts @@ -228,6 +228,7 @@ export default createGlobalStyle` } body { + background: #f5f3f1; height: 100%; margin: 0; } @@ -301,8 +302,6 @@ export default createGlobalStyle` } main { - min-height: 100vh; - > section { margin-bottom: 2em; } diff --git a/packages/react-query/src/NodeVersion.tsx b/packages/react-query/src/NodeVersion.tsx index 2d1c1183fd..156e1df655 100644 --- a/packages/react-query/src/NodeVersion.tsx +++ b/packages/react-query/src/NodeVersion.tsx @@ -13,10 +13,11 @@ interface Props { function NodeVersion ({ children, className = '', label }: Props): React.ReactElement { const { systemVersion } = useApi(); + const displayVersion = systemVersion.split('-').filter((_, index) => index <= 1).join('-'); return (
    - {label || ''}{systemVersion}{children} + {label || ''}{displayVersion}{children}
    ); }