feat(#304) Environments color 🎨 (#1053)

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use StyledWrapper

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

don't save anything for color if it is not set

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use redux store instead of local state

remove logs

fix selectedEnvironment

cleanup

add bottom border on active tab

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* move dependency to appropriate package.json

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* use border instead of background color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* simplify onColorChange

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* add black, keep backgound on unselected color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* fix conflicts

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use StyledWrapper

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

don't save anything for color if it is not set

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use redux store instead of local state

remove logs

fix selectedEnvironment

cleanup

add bottom border on active tab

# Conflicts:
#	packages/bruno-app/src/components/Environments/EnvironmentSelector/StyledWrapper.js
#	packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js
#	packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js

* Update packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentColor/index.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* unused selectedEnvironment prop in EnvironmentList

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

* RequestTab, avoid unnecessary call if undefined activeCollection

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

* use @uiw/reac-color instead of react-color

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

---------

Signed-off-by: mathieu <mathieu.dreano@gmail.com>
Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>
Co-authored-by: Mathieu D <mathieu.dreano@decathlon.com>
Co-authored-by: Anoop M D <anoop@usebruno.com>
Co-authored-by: Mathieu DREANO <122891400+mdreano@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Mathieu DREANO
2026-01-29 11:51:39 +01:00
committed by Sid
parent 214e1434e5
commit 5a6714f085
15 changed files with 612 additions and 24 deletions

439
package-lock.json generated
View File

@@ -10422,6 +10422,420 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@uiw/color-convert": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.9.2.tgz",
"integrity": "sha512-ibw9OS29S7GlL+vDwU3p5XG3vhR7XdzUecydpZbakUeg2Td6nfsnrCAX9sbLwQ73p0abO42v+V4qRaWq+7/BjQ==",
"license": "MIT",
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
},
"peerDependencies": {
"@babel/runtime": ">=7.19.0"
}
},
"node_modules/@uiw/react-color": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-2.9.2.tgz",
"integrity": "sha512-nIrw4Ol6boAkr1CUcCAkrUFVrKbT9T7/0qaSDpXmiDgKYf77gbXTWTsqVuXVDCSoCn28LvurpASS4AW8oiSBtg==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2",
"@uiw/react-color-block": "2.9.2",
"@uiw/react-color-chrome": "2.9.2",
"@uiw/react-color-circle": "2.9.2",
"@uiw/react-color-colorful": "2.9.2",
"@uiw/react-color-compact": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2",
"@uiw/react-color-editable-input-hsla": "2.9.2",
"@uiw/react-color-editable-input-rgba": "2.9.2",
"@uiw/react-color-github": "2.9.2",
"@uiw/react-color-hue": "2.9.2",
"@uiw/react-color-material": "2.9.2",
"@uiw/react-color-name": "2.9.2",
"@uiw/react-color-saturation": "2.9.2",
"@uiw/react-color-shade-slider": "2.9.2",
"@uiw/react-color-sketch": "2.9.2",
"@uiw/react-color-slider": "2.9.2",
"@uiw/react-color-swatch": "2.9.2",
"@uiw/react-color-wheel": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.9.2.tgz",
"integrity": "sha512-a2ACkE2vZIS4xnN9DaRfkQtAX/t8oK5NRSbX2QeOL23WIMHP1VNs7Yq5gXB68RHYenFgvs2JHuMOxZ2mK1W5Mw==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-drag-event-interactive": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.9.2.tgz",
"integrity": "sha512-0EIZTELA5pnxyMlBOFo3hrpy73db+Qeq6E+QptNfD/8izor8OvY1Uquj2VqD6gDz+iVHMELIoKxpaQ8sZR7NOg==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2",
"@uiw/react-color-swatch": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.9.2.tgz",
"integrity": "sha512-p7OZB7VWrkVbHxcTHsAq5U2vt3hAP3VvKEiDi592LKxS11IMnSd15ta8ngbJaXZWatqEpJSNgj12581yHtx+Bg==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2",
"@uiw/react-color-editable-input-hsla": "2.9.2",
"@uiw/react-color-editable-input-rgba": "2.9.2",
"@uiw/react-color-github": "2.9.2",
"@uiw/react-color-hue": "2.9.2",
"@uiw/react-color-saturation": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.9.2.tgz",
"integrity": "sha512-7XaeX3LfCRkZKffHL/KtYps7I9hNpmx9sJOuwi0ML+3urToFD8s7Iuq3upYZt8REYt1Y84SBjuUqx2YYmjUEjA==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-swatch": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.9.2.tgz",
"integrity": "sha512-tz/xeHayna2wpLipkZmcMgL1rmLMxfAmlOyBhUeWrpvqb9Fx59C/wL+5IYJA4rdsQvr9WyWjWmU/GhVKsEkW9w==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2",
"@uiw/react-color-hue": "2.9.2",
"@uiw/react-color-saturation": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.9.2.tgz",
"integrity": "sha512-fSSgRRBjkYuGRebZ7XM5XGVFvFwEyEGaV6mOhUpr3JFKhIES0/9oPbd80GDbRdj57Zxxrj76MCtd/aCFfwQSWA==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2",
"@uiw/react-color-editable-input-rgba": "2.9.2",
"@uiw/react-color-swatch": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.9.2.tgz",
"integrity": "sha512-DY7pu12+LDRn6cxDMvsy1/quaPTxicAPz/kfODV7KBf8+Hq4rFWeJ4KS6m22IKIbQxrBQgmQG0WFJLaPeY7cPw==",
"license": "MIT",
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.9.2.tgz",
"integrity": "sha512-kZfj3W20msLeP8/HY498rG30eBHRPAyxduhu94HLa9XggT/0ogwA9xZJZgWd6B7FYPeRlhRDY7dnF7caND63GQ==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-editable-input-rgba": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.9.2.tgz",
"integrity": "sha512-sQW3tSao754954aQuVK4qvn1i+KC2piE4UftaBubD3QxC02gg5VfgZRoI6AV+nLr73Ifv3mCXewjN1BcP/+x4A==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.9.2.tgz",
"integrity": "sha512-pKR94swzWxqRFUEZJBQ8WHcw6CklxLWhDyjvGpxiieAlwUAL0mlmtCcctRgsmJRuAKQlZx4WNslRgNX5aVcoZw==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-swatch": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.9.2.tgz",
"integrity": "sha512-vDGN5YCzw09BfxkQeDvAeQ7zAy141uJ3HkFk1lsXL7ha8xZ35AItE1s/C6d60vFjGdoloKShh0yA7df3pnjmxA==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.9.2.tgz",
"integrity": "sha512-R9MI9IlTof/L1rdxUFQAWgAgUSNJGXQsPujo8UGpwR7o5d+A3wwybUnPBsGKRnZwDy5zW7x+lPxY46GXE9aU9Q==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2",
"@uiw/react-color-editable-input-rgba": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-name/-/react-color-name-2.9.2.tgz",
"integrity": "sha512-knqRUkAe3pv6rB+tGzaURtwQkBqjRG62YNlzUx8Ty7g+pskWpLSPiMikW+9H5sLPq7wU3ichZiygqIp4BRgQzA==",
"license": "MIT",
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.9.2.tgz",
"integrity": "sha512-w1aUU+g6Axwbr1nLvF8k/zg5v7UW8z80eH6C7w+tdiOFOQKkKQlXqeOG0IIUUIj3v/ji+yM90IuOH+Ku7zsJrg==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-drag-event-interactive": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.9.2.tgz",
"integrity": "sha512-VE59rWv5ixqCN2CTpoe33j4SOCGU62bKguizx4HxgKczE/X0ySeEas8iP5XLg/4fYWl3EZN4uI+M8mNRnB0DPw==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.9.2.tgz",
"integrity": "sha512-PEvwaqDVDdBI/+fWASBWQOvx3ows7dIcv6Z06VHgEXk2chi95Fkrbd0YUUXMcp7ESsmUK1j5ozGMLAf9Nvx6Nw==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2",
"@uiw/react-color-editable-input": "2.9.2",
"@uiw/react-color-editable-input-rgba": "2.9.2",
"@uiw/react-color-hue": "2.9.2",
"@uiw/react-color-saturation": "2.9.2",
"@uiw/react-color-swatch": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.9.2.tgz",
"integrity": "sha512-vsi0AwmFJpb+flF8XCkacbX+MwLGOzrDKMqR29XE5sO8ERaezoT5mmYXzXXFcjyZYIuXse4C3JT38nsmOBp1vQ==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-color-alpha": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.9.2.tgz",
"integrity": "sha512-6zBy+E9NzZR672M2wPsbbNRqKy9Wi9jOuuxxyzov1CEZp+pPX7UwMlCX6RUhKdO0PzTSPCVQmbz5bplu5vsW0w==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.9.2.tgz",
"integrity": "sha512-ayGzQyMZM3Cp+sX7LNElQ/QQWMO7YG4k/RQwVJAhxNQ+4lJ/p4LLSnI85D7NxILkk+jiXnjxRroxxZ2eJhWo+g==",
"license": "MIT",
"dependencies": {
"@uiw/color-convert": "2.9.2",
"@uiw/react-drag-event-interactive": "2.9.2"
},
"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.9.2",
"resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.9.2.tgz",
"integrity": "sha512-6gxQz+Ij7JkXlEOpfZhOu+Gdp/sI9VnMeDl8AJeYl3+0YXP31lXGmyb0NkNYnoUmJO+RrAf68c1raMpaDWs+Ow==",
"license": "MIT",
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
},
"peerDependencies": {
"@babel/runtime": ">=7.19.0",
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
}
},
"node_modules/@usebruno/app": {
"resolved": "packages/bruno-app",
"link": true
@@ -13346,6 +13760,30 @@
"dev": true,
"license": "MIT"
},
"node_modules/colors-named": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.4.tgz",
"integrity": "sha512-R254qrKSxFJNa7QmM7vrRaz5Hygr7MIaNbXcIx7WfmlYJ9OjZQ+aczGlnKS8lLtNT0GM9aGZ4EcmNXrh5ttv6g==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/colors-named-hex": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.3.tgz",
"integrity": "sha512-vhUoMdCdOKgD9Ni3p6uV3ET1JJCHzlcK6lN3/yl+6TUHinDE6HUFlmnvkh/NDZ2M9049Ipn3mX85qu6akRiC1g==",
"license": "MIT",
"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",
@@ -30018,6 +30456,7 @@
"@tabler/icons": "^1.46.0",
"@testing-library/user-event": "^14.6.1",
"@tippyjs/react": "^4.2.6",
"@uiw/react-color": "^2.9.2",
"@usebruno/common": "0.1.0",
"@usebruno/graphql-docs": "0.1.0",
"@usebruno/schema": "0.7.0",

View File

@@ -18,6 +18,7 @@
"@tabler/icons": "^1.46.0",
"@testing-library/user-event": "^14.6.1",
"@tippyjs/react": "^4.2.6",
"@uiw/react-color": "^2.9.2",
"@usebruno/common": "0.1.0",
"@usebruno/graphql-docs": "0.1.0",
"@usebruno/schema": "0.7.0",

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { IconPlus, IconDownload, IconSettings } from '@tabler/icons';
import { IconPlus, IconDownload, IconSettings, IconDatabase } from '@tabler/icons';
import ToolHint from 'components/ToolHint';
const EnvironmentListContent = ({
@@ -38,6 +38,7 @@ const EnvironmentListContent = ({
data-tooltip-content={env.name}
data-tooltip-hidden={env.name?.length < 90}
>
<IconDatabase size={16} strokeWidth={1.5} color={env.color} />
<span className="max-w-100% truncate no-wrap">{env.name}</span>
</div>
))}

View File

@@ -5,8 +5,8 @@ const Wrapper = styled.div`
border-radius: ${(props) => props.theme.border.radius.base};
padding: 0.25rem 0.3rem 0.25rem 0.5rem;
user-select: none;
background-color: ${(props) => props.theme.app.collection.toolbar.environmentSelector.bg};
border: 1px solid ${(props) => props.theme.app.collection.toolbar.environmentSelector.border};
background-color: ${(props) => props.color ? undefined : 'transparent'};
border: 2px solid ${(props) => props.color ?? props.theme.dropdown.selectedColor};
line-height: 1rem;
transition: all 0.15s ease;
@@ -15,6 +15,11 @@ const Wrapper = styled.div`
background-color: ${(props) => props.theme.app.collection.toolbar.environmentSelector.hoverBg};
}
.active-env-toolhint {
display: flex;
flex-direction: row;
}
.caret {
margin-left: 0.25rem;
color: ${(props) => props.theme.app.collection.toolbar.environmentSelector.caret};
@@ -24,11 +29,12 @@ const Wrapper = styled.div`
.env-icon {
margin-right: 0.25rem;
color: ${(props) => props.theme.app.collection.toolbar.environmentSelector.icon};
color: ${(props) => props.color ?? props.theme.dropdown.selectedColor};
}
.env-text {
color: ${(props) => props.theme.app.collection.toolbar.environmentSelector.text};
color: ${(props) => props.color ?? props.theme.dropdown.selectedColor};
font-size: ${(props) => props.theme.font.size.base};
display: block;
}
@@ -65,6 +71,37 @@ const Wrapper = styled.div`
overflow: hidden;
}
.tippy-box .tippy-content {
padding: 0;
display: flex;
flex-direction: column;
height: 100%;
.dropdown-item {
display: flex;
flex-direction: row;
align-items: center;
column-gap: 0.35em;
padding: 0.35rem 0.6rem;
cursor: pointer;
font-size: ${(props) => props.theme.font.size.base};
color: ${(props) => props.theme.dropdown.primaryText};
&:hover:not(:disabled) {
background-color: ${(props) => props.theme.dropdown.hoverBg};
}
&.active {
background-color: ${(props) => props.theme.dropdown.selectedBg};
color: ${(props) => props.color ?? props.theme.dropdown.selectedColor} !important;
}
&.no-environment {
color: ${(props) => props.theme.dropdown.mutedText};
}
}
}
.configure-button {
position: absolute;
bottom: 0;

View File

@@ -134,14 +134,15 @@ const EnvironmentSelector = ({ collection }) => {
{activeCollectionEnvironment && (
<>
<div className="flex items-center">
<IconDatabase size={14} strokeWidth={1.5} className="env-icon" />
<ToolHint
className="active-env-toolhint"
text={activeCollectionEnvironment.name}
toolhintId={`collection-env-${activeCollectionEnvironment.uid}`}
place="bottom-start"
delayShow={1000}
hidden={activeCollectionEnvironment.name?.length < 7}
>
<IconDatabase size={14} strokeWidth={1.5} className="env-icon" />
<span className="env-text max-w-24 truncate overflow-hidden">{activeCollectionEnvironment.name}</span>
</ToolHint>
</div>
@@ -182,7 +183,7 @@ const EnvironmentSelector = ({ collection }) => {
});
return (
<StyledWrapper width={dropdownWidth}>
<StyledWrapper color={activeCollectionEnvironment?.color} width={dropdownWidth}>
<div className="environment-selector flex align-center cursor-pointer">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
{/* Tab Headers */}

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { useCallback } from 'react';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import { saveEnvironmentColor } from 'providers/ReduxStore/slices/collections/actions';
import { Circle } from '@uiw/react-color';
const EnvironmentColor = ({ environment, collectionUid }) => {
const dispatch = useDispatch();
const onColorChange = useCallback(
(color) => {
if (color == environment.color) return;
dispatch(saveEnvironmentColor(color, environment.uid, collectionUid))
.then(() => toast.success('Environment color changed successfully'))
.catch(() => toast.error('An error occurred while changing the environment color'));
},
[dispatch, environment.uid, environment.color, collectionUid]
);
return (
<Circle
id="environment-color"
style={{ gap: 3 }}
pointProps={{ style: { width: 14, height: 14, borderRadius: 10 } }}
colors={['#000000','#9c27b0','#3f51b5','#03a9f4','#009688','#8bc34a','#ffeb3b','#ff9800','#ff5722','#795548','#607d8b']}
color={environment.color}
onChange={(color) => onColorChange(color.hex)}
/>
);
};
export default EnvironmentColor;

View File

@@ -7,6 +7,8 @@ import toast from 'react-hot-toast';
import CopyEnvironment from 'components/Environments/EnvironmentSettings/CopyEnvironment';
import DeleteEnvironment from 'components/Environments/EnvironmentSettings/DeleteEnvironment';
import EnvironmentVariables from './EnvironmentVariables';
import EnvironmentColor from '../EnvironmentDetails/EnvironmentColor';
import ToolHint from 'components/ToolHint/index';
import StyledWrapper from './StyledWrapper';
const EnvironmentDetails = ({ environment, setIsModified, collection }) => {
@@ -119,7 +121,6 @@ const EnvironmentDetails = ({ environment, setIsModified, collection }) => {
{openCopyModal && (
<CopyEnvironment onClose={() => setOpenCopyModal(false)} environment={environment} collection={collection} />
)}
<div className="header">
<div className={`title-container ${isRenaming ? 'renaming' : ''}`}>
{isRenaming ? (
@@ -174,9 +175,8 @@ const EnvironmentDetails = ({ environment, setIsModified, collection }) => {
</div>
</div>
<div className="content">
<EnvironmentVariables environment={environment} setIsModified={setIsModified} collection={collection} />
</div>
<EnvironmentColor environment={environment} collectionUid={collection.uid} />
<EnvironmentVariables environment={environment} collection={collection} setIsModified={setIsModified} onClose={onClose} />
</StyledWrapper>
);
};

View File

@@ -1,4 +1,5 @@
import React, { useEffect, useState, useRef } from 'react';
import { findEnvironmentInCollection, findItem } from 'utils/collections';
import usePrevious from 'hooks/usePrevious';
import EnvironmentDetails from './EnvironmentDetails';
import CreateEnvironment from 'components/Environments/EnvironmentSettings/CreateEnvironment';
@@ -24,6 +25,10 @@ const EnvironmentList = ({
}) => {
const dispatch = useDispatch();
const EnvironmentList = ({ collection, isModified, setIsModified, onClose, setShowExportModal }) => {
const { environments, activeEnvironmentUid } = collection;
const [selectedEnvironment, setSelectedEnvironment] = useState(null);
const [openCreateModal, setOpenCreateModal] = useState(false);
const [openImportModal, setOpenImportModal] = useState(false);
const [searchText, setSearchText] = useState('');
@@ -38,7 +43,7 @@ const EnvironmentList = ({
const [switchEnvConfirmClose, setSwitchEnvConfirmClose] = useState(false);
const [originalEnvironmentVariables, setOriginalEnvironmentVariables] = useState([]);
const envUids = environments ? environments.map((env) => env.uid) : [];
const envUids = environments?.map((env) => env.uid) ?? [];
const prevEnvUids = usePrevious(envUids);
useEffect(() => {
@@ -63,10 +68,12 @@ const EnvironmentList = ({
if (hasSelectedEnvironmentChanged || selectedEnvironment.uid !== _selectedEnvironment?.uid) {
setSelectedEnvironment(_selectedEnvironment);
}
setOriginalEnvironmentVariables(_selectedEnvironment?.variables || []);
setOriginalEnvironmentVariables(selectedEnvironment?.variables||[]);
setSelectedEnvironment(findItem(environments, selectedEnvironment.uid));
return;
}
const environment = environments?.find((env) => env.uid === activeEnvironmentUid) || environments?.[0];
setSelectedEnvironment(environment);
@@ -74,15 +81,21 @@ const EnvironmentList = ({
}, [environments, activeEnvironmentUid, selectedEnvironment]);
useEffect(() => {
if (prevEnvUids && prevEnvUids.length && envUids.length > prevEnvUids.length) {
if (selectedEnvironment) {
setSelectedEnvironment(findEnvironmentInCollection(collection, selectedEnvironment.uid));
}
}, [environments]);
useEffect(() => {
if (prevEnvUids?.length && envUids.length > prevEnvUids.length) {
const newEnv = environments.find((env) => !prevEnvUids.includes(env.uid));
if (newEnv) {
setSelectedEnvironment(newEnv);
}
}
if (prevEnvUids && prevEnvUids.length && envUids.length < prevEnvUids.length) {
setSelectedEnvironment(environments && environments.length ? environments[0] : null);
if (prevEnvUids?.length && envUids.length < prevEnvUids.length) {
setSelectedEnvironment(environments?.length ? environments[0] : null);
}
}, [envUids, environments, prevEnvUids]);

View File

@@ -71,9 +71,8 @@ const Wrapper = styled.div`
&:not(.active) {
background: ${(props) => props.theme.requestTabs.bg};
border-color: transparent;
border-bottom: 3px solid ${(props) => props.color ?? "transparent"};
border-radius: ${(props) => props.theme.border.radius.base};
}
&:nth-last-child(1) {
@@ -113,7 +112,7 @@ const Wrapper = styled.div`
&.active {
background: ${(props) => props.theme.bg || '#ffffff'};
border: 1px solid ${(props) => props.theme.requestTabs.bottomBorder};
border-bottom-color: ${(props) => props.theme.bg || '#ffffff'};
border-bottom-color: ${(props) => props.color ?? props.theme.bg ?? '#ffffff'};
border-radius: 8px 8px 0 0;
z-index: 1;
margin-bottom: -2px;

View File

@@ -6,6 +6,7 @@ import { IconChevronRight, IconChevronLeft } from '@tabler/icons';
import { useSelector, useDispatch } from 'react-redux';
import { focusTab, reorderTabs } from 'providers/ReduxStore/slices/tabs';
import NewRequest from 'components/Sidebar/NewRequest';
import { findEnvironmentInCollection } from 'utils/collections';
import CollectionToolBar from './CollectionToolBar';
import RequestTab from './RequestTab';
import StyledWrapper from './StyledWrapper';
@@ -85,6 +86,17 @@ const RequestTabs = () => {
return null;
}
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
if (!activeTab) {
return <StyledWrapper>Something went wrong!</StyledWrapper>;
}
const activeCollection = find(collections, (c) => c.uid === activeTab.collectionUid);
const activeEnvironment = activeCollection
? findEnvironmentInCollection(activeCollection, activeCollection.activeEnvironmentUid)
: null;
const collectionRequestTabs = filter(tabs, (t) => t.collectionUid === activeTab.collectionUid);
const effectiveSidebarWidth = sidebarCollapsed ? 0 : leftSidebarWidth;
const maxTablistWidth = screenWidth - effectiveSidebarWidth - 150;
@@ -102,9 +114,14 @@ const RequestTabs = () => {
});
};
const getRootClassname = () => {
return classnames({
'has-chevrons': showChevrons
});
};
// Todo: Must support ephemeral requests
return (
<StyledWrapper>
<StyledWrapper color={activeEnvironment?.color} className={getRootClassname()}>
{newRequestModalOpen && (
<NewRequest collectionUid={activeCollection?.uid} onClose={() => setNewRequestModalOpen(false)} />
)}

View File

@@ -36,6 +36,7 @@ import {
sortCollections as _sortCollections,
updateCollectionMountStatus,
moveCollection,
saveEnvironmentColor as _saveEnvironmentColor,
workspaceEnvUpdateEvent,
requestCancelled,
resetRunResults,
@@ -2252,6 +2253,27 @@ export const mergeAndPersistEnvironment
});
};
export const saveEnvironmentColor = (color, environmentUid, collectionUid) => (dispatch, getState) => {
return new Promise((resolve, reject) => {
const collection =
findCollectionByUid(getState().collections.collections, collectionUid) ??
reject(new Error('Collection not found'));
const environment =
findEnvironmentInCollection(collection, environmentUid) ?? reject(new Error('Environment not found'));
const updatedEnvironment = { ...environment, color: color };
const { ipcRenderer } = window;
environmentSchema
.validate(updatedEnvironment)
// save to file
.then(() => ipcRenderer.invoke('renderer:save-environment', collection.pathname, updatedEnvironment))
// update store
.then(() => dispatch(_saveEnvironmentColor({ color, environmentUid, collectionUid })))
.then(resolve)
.catch(reject);
});
};
export const selectEnvironment = (environmentUid, collectionUid) => (dispatch, getState) => {
return new Promise((resolve, reject) => {
const state = getState();

View File

@@ -258,6 +258,17 @@ export const collectionsSlice = createSlice({
if (environment) {
environment.variables = variables;
environment.color = color;
}
}
},
saveEnvironmentColor: (state, action) => {
const { color, environmentUid, collectionUid } = action.payload;
const collection = findCollectionByUid(state.collections, collectionUid);
if (collection) {
const environment = findEnvironmentInCollection(collection, environmentUid);
if (environment) {
environment.color = color;
}
}
},
@@ -3464,6 +3475,7 @@ export const {
updatedFolderSettingsSelectedTab,
collectionUnlinkEnvFileEvent,
saveEnvironment,
saveEnvironmentColor,
selectEnvironment,
newItem,
deleteItem,

View File

@@ -12,7 +12,7 @@ const _ = require('lodash');
// }
const indentLevel = 4;
const grammar = ohm.grammar(`Bru {
BruEnvFile = (vars | secretvars)*
BruEnvFile = (vars | secretvars | color)*
nl = "\\r"? "\\n"
st = " " | "\\t"
@@ -43,6 +43,7 @@ const grammar = ohm.grammar(`Bru {
secretvars = "vars:secret" array
vars = "vars" dictionary
color = "color:" any*
}`);
const mapPairListToKeyValPairs = (pairList = []) => {
@@ -190,6 +191,11 @@ const sem = grammar.createSemantics().addAttribute('ast', {
return {
variables: vars
};
},
color: (_1, anystring) => {
return {
color: anystring.sourceString.trim()
};
}
});

View File

@@ -20,13 +20,16 @@ const envToJson = (json) => {
return indentString(`${prefix}${name}`);
});
const color = _.get(json, 'color', undefined);
let output = '';
if (!variables || !variables.length) {
return `vars {
output += `vars {
}
`;
}
let output = '';
if (vars.length) {
output += `vars {
${vars.join('\n')}
@@ -38,6 +41,10 @@ ${vars.join('\n')}
output += `vars:secret [
${secretVars.join(',\n')}
]
`;
}
if (color) {
output += `color: ${color}
`;
}

View File

@@ -16,7 +16,8 @@ const environmentVariablesSchema = Yup.object({
const environmentSchema = Yup.object({
uid: uidSchema,
name: Yup.string().min(1).required('name is required'),
variables: Yup.array().of(environmentVariablesSchema).required('variables are required')
variables: Yup.array().of(environmentVariablesSchema).required('variables are required'),
color: Yup.string().optional()
})
.noUnknown(true)
.strict();