diff --git a/package-lock.json b/package-lock.json index bd1bebf..dd65c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "@tiptap/starter-kit": "^2.5.9", "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", + "@uiw/react-color": "^2.5.1", "adm-zip": "^0.5.16", "asmcrypto.js": "2.3.2", "axios": "^1.7.7", @@ -5900,6 +5901,399 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/color-convert": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.5.1.tgz", + "integrity": "sha512-p+P8Ho0Z1AbUprES0hcLEDAaXbGH92TmjckkRQZ5S7HcyQ+9ZXlSsDFILjFbYu/okVjx5VG59T57Dx84lv9AWA==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0" + } + }, + "node_modules/@uiw/react-color": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-2.5.1.tgz", + "integrity": "sha512-u6Kj7rdhsMOls2KItpHLkG8WTghDS2jYBucLeOLLJXJDs25TuEBI9d1o939og8cUJtTwBrowWFFU63a1kGsciA==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-block": "2.5.1", + "@uiw/react-color-chrome": "2.5.1", + "@uiw/react-color-circle": "2.5.1", + "@uiw/react-color-colorful": "2.5.1", + "@uiw/react-color-compact": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-hsla": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-github": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-material": "2.5.1", + "@uiw/react-color-name": "2.5.1", + "@uiw/react-color-saturation": "2.5.1", + "@uiw/react-color-shade-slider": "2.5.1", + "@uiw/react-color-sketch": "2.5.1", + "@uiw/react-color-slider": "2.5.1", + "@uiw/react-color-swatch": "2.5.1", + "@uiw/react-color-wheel": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-alpha": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.5.1.tgz", + "integrity": "sha512-hPsIgsnuOQrqinXt3Gt+87fHudbUvvPW+TpvRY0HS9v4ptFu5UsCc/7DPTVKTaL+p+0oaA6eTbziLzPLRLzgsQ==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-drag-event-interactive": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-block": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.5.1.tgz", + "integrity": "sha512-qvubiV0z0P3OxpNt6o1UQ3CVsjVBY1/n/oz6Gzzxx9YPqSClI04AtFjwOQxF7M17SYqXv+88y77gfEfPIqk5+A==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-chrome": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.5.1.tgz", + "integrity": "sha512-m/CyRaWgmkW5aQTQ8AZwyvopYm+bhvX06uS+ezQjXDYDtjLvq7RbM0JLLNIOyMXke964R58fhoX4G06ZWd8ycA==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-hsla": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-github": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-saturation": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-circle": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.5.1.tgz", + "integrity": "sha512-+8zb/Ork1Q5f2bq0jN+GF7OyqY+2ZDYGrdZovN3EBZLMmERbg6TM2+1gTweeFsdiEM/gpteupJpwKpO1aBCocg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-colorful": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.5.1.tgz", + "integrity": "sha512-Y/8Y2Kman6IZQpgs4tPTGPuTNr3fJIJxf4f13jll6xuaOsVZeDq9q+DlMErggL+5ICtaBr8gG+w68nCiY+QqKg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-saturation": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-compact": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.5.1.tgz", + "integrity": "sha512-5jHJcXEkjMwcghzCgSBU2rPMVjuuaJ7B6IxypNkafRQ4FkW/6bP9WpPkzcNXCZ/gPvSJ1OMQ+Y600mdO78qG5Q==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.5.1.tgz", + "integrity": "sha512-0kr5vQJGPln8LObXwfI2YLiHFz2DW3Atgi51JXlrZUyyaVujXRgMTAc1fz/1RQR6cU2A4bweFaCQljcTsv+Cdg==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input-hsla": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.5.1.tgz", + "integrity": "sha512-gmnXB6JrYFAd8VN/EfNDJaTdkFHAnUxjzcsQjQyOEr046jDjWgEc/5o2uE1LwIvoJNg9Lo6LYsr37LnFWwsiLw==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input-rgba": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.5.1.tgz", + "integrity": "sha512-rk6OxL9lTdRI45aNe3GbUghvaELk4knkEf0gvF/mPHxoeE+nNphSrO5gHm3HhoDOgaplp81VP3q4gUwcdjBzvw==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-github": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.5.1.tgz", + "integrity": "sha512-t05rIy2ifReiVnjv3x+IVlJH7wvwtZugMeouDa/1Y7jIGZswO0zw3zMxz7qfHrzf5NVYWjmEF8QCj85ngv9brg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-hue": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.5.1.tgz", + "integrity": "sha512-o7mjZhm+U4gHxaBXFxjPINeE3jWfiZAl7RUFqwn4PDZC8wvhU5hEKgJUvcXzErYro0ZYrE1fC/wUHRpI+vcEBg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-material": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.5.1.tgz", + "integrity": "sha512-iPB4YfKVTNO1lSIQ16DMdDurDKvGTjv6Qwi/nq47yE3nnhB0YbOFwb/IZbWBS1sCTPx1an7dM2IZ+hYoYcjrXg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-name": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-name/-/react-color-name-2.5.1.tgz", + "integrity": "sha512-JFb6DFz9kF2jI42MS/vtXZu1XzIrzcSIOqCwVkYWCQnSxOM9h+vd4pv2Yi1oy7IPgaadXUDkrGQSAvEkXU593Q==", + "dependencies": { + "colors-named": "^1.0.1", + "colors-named-hex": "^1.0.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0" + } + }, + "node_modules/@uiw/react-color-saturation": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.5.1.tgz", + "integrity": "sha512-mQ6eGmn6dUXfScQrb5tP0TBGCpZWzrQuYOAiwK9u31IJaxFwD1NNAzkiienWe4MQkA5zmgz7Ol6FEdLN8K+vGw==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-drag-event-interactive": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-shade-slider": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.5.1.tgz", + "integrity": "sha512-hrscAmqmy/Od/usUPETaEuvsNRhUGvNArl73d7HK6e6FjbRFPDBq40LkvjETe8BJMbxrBXTMo6dK7DO08lYq9g==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-sketch": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.5.1.tgz", + "integrity": "sha512-eQgAnlSZvqoTt6frZa/j+tFdaIBEFneIdxEUfidD8hwvyu5OR/WLHnDy/4fYAxhehDp9Ej8eS3ZsCgPACBMOtA==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-saturation": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-slider": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.5.1.tgz", + "integrity": "sha512-2yluI0Akp6UMXTeAJ4CEjL8flhIFpn3xUPsFXbQmBSzMYJygleVFmwhMye8LSA2PCe3UdaqA2cWXxWsTL0FbIg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-swatch": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.5.1.tgz", + "integrity": "sha512-EQ7UEzxdohfsdpXmcEWNmK/uiznZovEKo6+j3OLrSU5pZGO7pxjR9sQMlscikvd8Mu1Mm3U0E6bJseo2acD4Lg==", + "dependencies": { + "@uiw/color-convert": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-wheel": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.5.1.tgz", + "integrity": "sha512-e3tDwDoC2T7zTapRRm/QxcOJ7IWJwNCoxZ/f97RL1Ib3gAN/k67H1bkR9TK7euRCUxGy031guxTgdKO9v19XFg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-drag-event-interactive": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-drag-event-interactive": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.5.1.tgz", + "integrity": "sha512-GNxhxk5L4O5Gpi20A/BG5sO0GNBNwtNWJidJsJu3pgHUBErN4rhqTDXXu3BQTz5C8yOG5D02Y6Zq/6yu6ckImw==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -7774,6 +8168,28 @@ "color-support": "bin.js" } }, + "node_modules/colors-named": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", + "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/colors-named-hex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", + "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", diff --git a/package.json b/package.json index 3c92ad4..3594023 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@tiptap/starter-kit": "^2.5.9", "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", + "@uiw/react-color": "^2.5.1", "adm-zip": "^0.5.16", "asmcrypto.js": "2.3.2", "axios": "^1.7.7", diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 3c53c52..cb66936 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -48,7 +48,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); - + const theme = useTheme(); const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ @@ -216,7 +216,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { maxHeight: '60vh', overflowY: 'auto', overflowX: 'hidden', - backgroundColor: 'rgb(30 30 32 / 70%)', + backgroundColor: theme.palette.background.paper, }} > {wallets?.map((wallet, idx) => { @@ -429,7 +429,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { bgcolor: theme.palette.background.default, flexGrow: 1, '&:hover': { - backgroundColor: theme.palette.background.paper, + backgroundColor: theme.palette.action.hover, transform: 'scale(1.01)', }, transition: 'all 0.1s ease-in-out', diff --git a/src/components/BuyQortInformation.tsx b/src/components/BuyQortInformation.tsx index cbcbe39..776eefb 100644 --- a/src/components/BuyQortInformation.tsx +++ b/src/components/BuyQortInformation.tsx @@ -110,7 +110,6 @@ export const BuyQortInformation = ({ balance }) => { { > - + - + diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 4d402b6..7942dd2 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -48,6 +48,7 @@ import { useVirtualizer } from '@tanstack/react-virtual'; import ErrorBoundary from '../../common/ErrorBoundary'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { getFee } from '../../background'; export const requestQueuePromos = new RequestQueueWithPromise(20); export function utf8ToBase64(inputString: string): string { diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 77032f7..3ac9a2e 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -18,6 +18,7 @@ import { TransitionProps } from '@mui/material/transitions'; import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; import { enabledDevModeAtom } from '../../atoms/global'; import { useRecoilState } from 'recoil'; +import ThemeManager from '../Theme/ThemeManager'; const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, @@ -185,6 +186,7 @@ export const Settings = ({ address, open, setOpen }) => { label="Enable dev mode" /> )} + diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index d2e7048..0f24b4a 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -6,57 +6,129 @@ import { useEffect, useCallback, } from 'react'; -import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; -import { darkTheme } from '../../styles/theme-dark'; -import { lightTheme } from '../../styles/theme-light'; +import { + ThemeProvider as MuiThemeProvider, + createTheme, +} from '@mui/material/styles'; +import { lightThemeOptions } from '../../styles/theme-light'; +import { darkThemeOptions } from '../../styles/theme-dark'; + +const defaultTheme = { + id: 'default', + name: 'Default Theme', + light: lightThemeOptions.palette, + dark: darkThemeOptions.palette, +}; const ThemeContext = createContext({ themeMode: 'light', toggleTheme: () => {}, + userThemes: [defaultTheme], + addUserTheme: (themes) => {}, + setUserTheme: (theme) => {}, + currentThemeId: 'default', }); -export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { +export const ThemeProvider = ({ children }) => { const [themeMode, setThemeMode] = useState('light'); + const [userThemes, setUserThemes] = useState([defaultTheme]); + const [currentThemeId, setCurrentThemeId] = useState('default'); - const theme = useMemo( - () => (themeMode === 'light' ? lightTheme : darkTheme), - [themeMode] - ); + const currentTheme = + userThemes.find((theme) => theme.id === currentThemeId) || defaultTheme; + + const muiTheme = useMemo(() => { + if (themeMode === 'light') { + return createTheme({ + ...lightThemeOptions, + palette: { + ...currentTheme.light, + }, + }); + } else { + return createTheme({ + ...lightThemeOptions, + palette: { + ...currentTheme.dark, + }, + }); + } + }, [themeMode, currentTheme]); + + const saveSettings = ( + themes = userThemes, + mode = themeMode, + themeId = currentThemeId + ) => { + localStorage.setItem( + 'saved_ui_theme', + JSON.stringify({ + mode, + userThemes: themes, + currentThemeId: themeId, + }) + ); + }; const toggleTheme = () => { - setThemeMode((prevMode) => { - const newMode = prevMode === 'light' ? 'dark' : 'light'; - - const themeProperties = { - mode: newMode, - }; - - localStorage.setItem('saved_ui_theme', JSON.stringify(themeProperties)); - + setThemeMode((prev) => { + const newMode = prev === 'light' ? 'dark' : 'light'; + saveSettings(userThemes, newMode, currentThemeId); return newMode; }); }; - const getSavedTheme = useCallback(async () => { - try { - const themeProperties = JSON.parse( - localStorage.getItem(`saved_ui_theme`) || '{}' - ); + const addUserTheme = (themes) => { + setUserThemes(themes); + saveSettings(themes); + }; - const theme = themeProperties?.mode || 'light'; - setThemeMode(theme); - } catch (error) { - console.log('error', error); + const setUserTheme = (theme) => { + if (theme.id === 'default') { + setCurrentThemeId('default'); + saveSettings(userThemes, themeMode, 'default'); + } else { + setCurrentThemeId(theme.id); + saveSettings(userThemes, themeMode, theme.id); + } + }; + + const loadSettings = useCallback(() => { + const saved = localStorage.getItem('saved_ui_theme'); + if (saved) { + try { + const parsed = JSON.parse(saved); + if (parsed.mode === 'light' || parsed.mode === 'dark') + setThemeMode(parsed.mode); + if (Array.isArray(parsed.userThemes)) { + const filteredThemes = parsed.userThemes.filter( + (theme) => theme.id !== 'default' + ); + setUserThemes([defaultTheme, ...filteredThemes]); + } + if (parsed.currentThemeId) setCurrentThemeId(parsed.currentThemeId); + } catch (error) { + console.error('Failed to parse saved_ui_theme:', error); + } } }, []); useEffect(() => { - getSavedTheme(); - }, [getSavedTheme]); + loadSettings(); + }, [loadSettings]); return ( - - {children} + + {children} ); }; diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx new file mode 100644 index 0000000..3b2b711 --- /dev/null +++ b/src/components/Theme/ThemeManager.tsx @@ -0,0 +1,309 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { + Box, + Button, + IconButton, + Typography, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + List, + ListItemText, + ListItemSecondaryAction, + TextField, + Tabs, + Tab, + ListItemButton, +} from '@mui/material'; +import { Sketch } from '@uiw/react-color'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import AddIcon from '@mui/icons-material/Add'; +import CheckIcon from '@mui/icons-material/Check'; +import { useThemeContext } from './ThemeContext'; +import { darkThemeOptions } from '../../styles/theme-dark'; +import { lightThemeOptions } from '../../styles/theme-light'; +import ShortUniqueId from 'short-unique-id'; +import { rgbStringToHsva, rgbaStringToHsva } from '@uiw/color-convert'; + +const uid = new ShortUniqueId({ length: 8 }); + +function detectColorFormat(color) { + if (typeof color !== 'string') return null; + if (color.startsWith('rgba')) return 'rgba'; + if (color.startsWith('rgb')) return 'rgb'; + return null; +} + +export default function ThemeManager() { + const { userThemes, addUserTheme, setUserTheme, currentThemeId } = + useThemeContext(); + const [openEditor, setOpenEditor] = useState(false); + const [themeDraft, setThemeDraft] = useState({ + id: '', + name: '', + light: {}, + dark: {}, + }); + const [currentTab, setCurrentTab] = useState('light'); + const nameInputRef = useRef(null); + + useEffect(() => { + if (openEditor && nameInputRef.current) { + nameInputRef.current.focus(); + } + }, [openEditor]); + + const handleAddTheme = () => { + setThemeDraft({ + id: '', + name: '', + light: structuredClone(lightThemeOptions.palette), + dark: structuredClone(darkThemeOptions.palette), + }); + setOpenEditor(true); + }; + + const handleEditTheme = (themeId) => { + const themeToEdit = userThemes.find((theme) => theme.id === themeId); + if (themeToEdit) { + setThemeDraft({ ...themeToEdit }); + setOpenEditor(true); + } + }; + + const handleSaveTheme = () => { + if (themeDraft.id) { + const updatedThemes = [...userThemes]; + const index = updatedThemes.findIndex( + (theme) => theme.id === themeDraft.id + ); + if (index !== -1) { + updatedThemes[index] = themeDraft; + addUserTheme(updatedThemes); + } + } else { + const newTheme = { ...themeDraft, id: uid.rnd() }; + const updatedThemes = [...userThemes, newTheme]; + addUserTheme(updatedThemes); + setUserTheme(newTheme); + } + setOpenEditor(false); + }; + + const handleDeleteTheme = (id) => { + const updatedThemes = userThemes.filter((theme) => theme.id !== id); + addUserTheme(updatedThemes); + + if (id === currentThemeId) { + // Find the default theme object in the list + const defaultTheme = updatedThemes.find( + (theme) => theme.id === 'default' + ); + + if (defaultTheme) { + setUserTheme(defaultTheme); + } else { + // Emergency fallback + setUserTheme({ + light: lightThemeOptions, + dark: darkThemeOptions, + }); + } + } + }; + + const handleApplyTheme = (theme) => { + setUserTheme(theme); + }; + + const handleColorChange = (mode, fieldPath, color) => { + setThemeDraft((prev) => { + const updated = { ...prev }; + const paths = fieldPath.split('.'); + updated[mode][paths[0]][paths[1]] = color.hex; + return updated; + }); + }; + + const renderColorPicker = (mode, label, fieldPath, currentValue) => { + let color = currentValue || '#ffffff'; + const format = detectColorFormat(currentValue); + if (format === 'rgba') { + color = rgbaStringToHsva(currentValue); + } else if (format === 'rgb') { + color = rgbStringToHsva(currentValue); + } + return ( + + + {label} + + handleColorChange(mode, fieldPath, color)} + /> + + ); + }; + + return ( + + + Theme Manager + + + + + + {userThemes?.map((theme, index) => ( + + + + {theme.id !== 'default' && ( + <> + handleEditTheme(theme.id)}> + + + handleDeleteTheme(theme.id)}> + + + + )} + handleApplyTheme(theme)}> + + + + + ))} + + + setOpenEditor(false)} + fullWidth + maxWidth="md" + > + + {themeDraft.id ? 'Edit Theme' : 'Add New Theme'} + + + + setThemeDraft((prev) => ({ ...prev, name: e.target.value })) + } + /> + + setCurrentTab(newValue)} + sx={{ mt: 2, mb: 2 }} + > + + + + + + {renderColorPicker( + currentTab, + 'Primary Main', + 'primary.main', + themeDraft[currentTab]?.primary?.main + )} + {renderColorPicker( + currentTab, + 'Primary Dark', + 'primary.dark', + themeDraft[currentTab]?.primary?.dark + )} + {renderColorPicker( + currentTab, + 'Primary Light', + 'primary.light', + themeDraft[currentTab]?.primary?.light + )} + {renderColorPicker( + currentTab, + 'Secondary Main', + 'secondary.main', + themeDraft[currentTab]?.secondary?.main + )} + {renderColorPicker( + currentTab, + 'Background Default', + 'background.default', + themeDraft[currentTab]?.background?.default + )} + {renderColorPicker( + currentTab, + 'Background Paper', + 'background.paper', + themeDraft[currentTab]?.background?.paper + )} + {renderColorPicker( + currentTab, + 'Background Surface', + 'background.surface', + themeDraft[currentTab]?.background?.surface + )} + {renderColorPicker( + currentTab, + 'Text Primary', + 'text.primary', + themeDraft[currentTab]?.text?.primary + )} + {renderColorPicker( + currentTab, + 'Text Secondary', + 'text.secondary', + themeDraft[currentTab]?.text?.secondary + )} + {renderColorPicker( + currentTab, + 'Border Main', + 'border.main', + themeDraft[currentTab]?.border?.main + )} + {renderColorPicker( + currentTab, + 'Border Subtle', + 'border.subtle', + themeDraft[currentTab]?.border?.subtle + )} + + + + + + + + + ); +} diff --git a/src/components/Theme/themeManager.css b/src/components/Theme/themeManager.css new file mode 100644 index 0000000..85a4538 --- /dev/null +++ b/src/components/Theme/themeManager.css @@ -0,0 +1,39 @@ +[data-color-mode*='dark'] .w-color-sketch { + --sketch-background: #323232 !important; +} + +[data-color-mode*='dark'] .w-color-swatch { + --sketch-swatch-border-top: 1px solid #525252 !important; +} + +[data-color-mode*='dark'] .w-color-block { + --block-background-color: #323232 !important; + --block-box-shadow: rgb(0 0 0 / 10%) 0 1px !important; +} + +[data-color-mode*='dark'] .w-color-editable-input { + --editable-input-label-color: #757575 !important; + --editable-input-box-shadow: #616161 0px 0px 0px 1px inset !important; + --editable-input-color: #bbb !important; +} + +[data-color-mode*='dark'] .w-color-github { + --github-border: 1px solid rgba(0, 0, 0, 0.2) !important; + --github-background-color: #323232 !important; + --github-box-shadow: rgb(0 0 0 / 15%) 0px 3px 12px !important; + --github-arrow-border-color: rgba(0, 0, 0, 0.15) !important; +} + +[data-color-mode*='dark'] .w-color-compact { + --compact-background-color: #323232 !important; +} + +[data-color-mode*='dark'] .w-color-material { + --material-background-color: #323232 !important; + --material-border-bottom-color: #707070 !important; +} + +[data-color-mode*='dark'] .w-color-alpha { + --alpha-pointer-background-color: #6a6a6a !important; + --alpha-pointer-box-shadow: rgb(0 0 0 / 37%) 0px 1px 4px 0px !important; +} diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index 971da81..d54a1c1 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -1,14 +1,14 @@ import { createTheme, ThemeOptions } from '@mui/material/styles'; import { commonThemeOptions } from './theme-common'; -const darkThemeOptions: ThemeOptions = { +export const darkThemeOptions: ThemeOptions = { ...commonThemeOptions, palette: { mode: 'dark', primary: { - main: 'rgb(46, 61, 96)', - dark: 'rgb(5, 20, 53)', - light: 'rgb(45, 92, 201)', + main: 'rgb(100, 155, 240)', + dark: 'rgb(45, 92, 201)', + light: 'rgb(130, 185, 255)', }, secondary: { main: 'rgb(69, 173, 255)', diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index dbba746..e66788f 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -1,7 +1,7 @@ import { createTheme, ThemeOptions } from '@mui/material/styles'; import { commonThemeOptions } from './theme-common'; -const lightThemeOptions: ThemeOptions = { +export const lightThemeOptions: ThemeOptions = { ...commonThemeOptions, palette: { mode: 'light', @@ -26,10 +26,6 @@ const lightThemeOptions: ThemeOptions = { main: 'rgba(0, 0, 0, 0.12)', subtle: 'rgba(0, 0, 0, 0.08)', }, - border: { - main: 'rgba(0, 0, 0, 0.12)', - subtle: 'rgba(0, 0, 0, 0.08)', - }, }, components: { MuiCard: {