diff --git a/src/renderer/src/components/ManifestInfo/index.less b/src/renderer/src/components/ManifestInfo/index.less index 235134b..c5a4f93 100644 --- a/src/renderer/src/components/ManifestInfo/index.less +++ b/src/renderer/src/components/ManifestInfo/index.less @@ -15,6 +15,9 @@ .manifest_info-icon { width: 80px; height: 80px; + min-width: 80px; + min-height: 80px; + max-height: 80px; border-radius: 12px; diff --git a/src/renderer/src/components/NewInstallation/index.jsx b/src/renderer/src/components/NewInstallation/index.jsx new file mode 100644 index 0000000..e46c897 --- /dev/null +++ b/src/renderer/src/components/NewInstallation/index.jsx @@ -0,0 +1,36 @@ +import React from "react" +import * as antd from "antd" + +import { Context as InstallationsContext } from "contexts/installations" + +import "./index.less" + +const NewInstallation = (props) => { + const { install } = React.useContext(InstallationsContext) + const [manifestUrl, setManifestUrl] = React.useState("") + + function handleClickInstall() { + install(manifestUrl) + props.close() + } + + return
+ { + setManifestUrl(e.target.value) + }} + onPressEnter={handleClickInstall} + /> + + + Install + +
+} + +export default NewInstallation \ No newline at end of file diff --git a/src/renderer/src/components/NewInstallation/index.less b/src/renderer/src/components/NewInstallation/index.less new file mode 100644 index 0000000..528ef27 --- /dev/null +++ b/src/renderer/src/components/NewInstallation/index.less @@ -0,0 +1,9 @@ +.new_installation_prompt { + display: flex; + flex-direction: column; + + align-items: center; + justify-content: center; + + gap: 20px; +} \ No newline at end of file diff --git a/src/renderer/src/components/PackageItem/index.jsx b/src/renderer/src/components/PackageItem/index.jsx new file mode 100644 index 0000000..5fa5d05 --- /dev/null +++ b/src/renderer/src/components/PackageItem/index.jsx @@ -0,0 +1,181 @@ +import React from "react" +import * as antd from "antd" +import classnames from "classnames" + +import BarLoader from "react-spinners/BarLoader" + +import { MdFolder, MdDelete, MdPlayArrow, MdUpdate, MdOutlineMoreVert, MdSettings, MdInfoOutline } from "react-icons/md" + +import "./index.less" + +const PackageItem = (props) => { + const [manifest, setManifest] = React.useState(props.manifest) + + const isLoading = manifest.status === "installing" || manifest.status === "uninstalling" || manifest.status === "updating" + const isInstalled = manifest.status === "installed" + const isFailed = manifest.status === "failed" + + const onClickUpdate = () => { + antd.Modal.confirm({ + title: "Update", + content: `Are you sure you want to update ${manifest.id}?`, + onOk: () => { + ipc.exec("bundle:update", manifest.id) + }, + }) + } + + const onClickPlay = () => { + ipc.exec("bundle:exec", manifest.id) + } + + const onClickFolder = () => { + ipc.exec("bundle:open", manifest.id) + } + + const onClickDelete = () => { + antd.Modal.confirm({ + title: "Uninstall", + content: `Are you sure you want to uninstall ${manifest.id}?`, + onOk: () => { + ipc.exec("bundle:uninstall", manifest.id) + }, + }) + } + + function handleUpdate(event, data) { + setManifest({ + ...manifest, + ...data, + }) + } + + function renderStatusLine(manifest) { + if (isLoading) { + return manifest.status + } + + return `v${manifest.version}` ?? "N/A" + } + + const MenuProps = { + items: [ + { + key: "open_folder", + label: "Open Folder", + icon: , + onClick: onClickFolder, + }, + { + key: "update", + label: "Update", + icon: , + onClick: onClickUpdate, + }, + { + key: "options", + label: "Options", + icon: , + disabled: true + }, + { + type: "divider" + }, + { + key: "info", + label: "Info", + icon: , + disabled: true + }, + { + key: "delete", + label: "Uninstall", + icon: , + onClick: onClickDelete, + danger: true, + }, + ], + } + + React.useEffect(() => { + ipc.on(`installation:${manifest.id}:status`, handleUpdate) + + return () => { + ipc.off(`installation:${manifest.id}:status`, handleUpdate) + } + }, []) + + React.useEffect(() => { + setManifest(props.manifest) + }, [props.manifest]) + + return
+
+ + +
+

+ { + manifest.pack_name + } +

+

+ { + renderStatusLine(manifest) + } +

+
+ +
+ { + isFailed && + Retry + + } + + { + isInstalled && manifest.executable && + + + } + + { + isInstalled && !manifest.executable && + } + type="primary" + /> + + } +
+
+ +
+ { + isLoading && + } + +

{manifest.statusText ?? "Unknown status"}

+
+
+} + +export default PackageItem \ No newline at end of file diff --git a/src/renderer/src/components/PackageItem/index.less b/src/renderer/src/components/PackageItem/index.less new file mode 100644 index 0000000..3feb589 --- /dev/null +++ b/src/renderer/src/components/PackageItem/index.less @@ -0,0 +1,137 @@ +@installation-item-borderRadius: 12px; +@installation-item-height: 60px; + +.installation_item_wrapper { + position: relative; + + display: flex; + flex-direction: column; + + &.status_visible { + .installation_item { + border-bottom: 1px solid var(--border-color); + } + + .installation_status { + height: fit-content; + + padding: 10px 20px; + padding-top: calc(8px + 10px); + + opacity: 1; + transform: translateY(-8px); + } + } + + &:nth-child(odd) { + .installation_item { + background-color: var(--background-color-primary); + } + + .installation_status { + background-color: var(--background-color-primary); + } + } + + .installation_item { + display: flex; + flex-direction: row; + + align-items: center; + + height: @installation-item-height; + max-height: @installation-item-height; + min-height: @installation-item-height; + + gap: 10px; + + padding: 5px; + + border-radius: @installation-item-borderRadius; + + background-color: var(--background-color-primary); + + z-index: 50; + + .installation_item_info { + display: flex; + flex-direction: column; + + gap: 10px; + + width: 100%; + + h2 { + display: inline-flex; + flex-direction: row; + + width: 100%; + } + + p { + font-size: 0.7rem; + text-transform: uppercase; + } + } + + .installation_item_icon { + width: 50px; + height: 50px; + + min-width: 50px; + min-height: 50px; + + background-color: var(--background-color-secondary); + + overflow: hidden; + + border-radius: 12px; + + img { + width: 100%; + height: 100%; + + object-fit: contain; + } + } + + .installation_item_actions { + display: flex; + + gap: 10px; + + align-items: center; + } + } + + .installation_status { + position: relative; + + z-index: 49; + + display: inline-flex; + flex-direction: column; + + background-color: var(--background-color-primary); + + gap: 10px; + + width: 100%; + + border-radius: 0 0 12px 12px; + + padding: 0; + margin: 0; + opacity: 0; + height: 0; + overflow: hidden; + + p { + font-size: 0.7rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + line-height: 14px; + } + } +} \ No newline at end of file diff --git a/src/renderer/src/pages/manager/index.jsx b/src/renderer/src/pages/manager/index.jsx index 007f6b8..d2cf428 100644 --- a/src/renderer/src/pages/manager/index.jsx +++ b/src/renderer/src/pages/manager/index.jsx @@ -1,176 +1,15 @@ import React from "react" import * as antd from "antd" -import classnames from "classnames" -import BarLoader from "react-spinners/BarLoader" - -import { MdAdd, MdFolder, MdDelete, MdPlayArrow, MdUpdate } from "react-icons/md" +import { MdAdd } from "react-icons/md" import { Context as InstallationsContext, WithContext } from "contexts/installations" +import PackageItem from "components/PackageItem" +import NewInstallation from "components/NewInstallation" + import "./index.less" -const NewInstallation = (props) => { - const { install } = React.useContext(InstallationsContext) - const [manifestUrl, setManifestUrl] = React.useState("") - - function handleClickInstall() { - install(manifestUrl) - props.close() - } - - return
- { - setManifestUrl(e.target.value) - }} - onPressEnter={handleClickInstall} - /> - - - Install - -
-} - -const InstallationItem = (props) => { - const [manifest, setManifest] = React.useState(props.manifest) - - const isLoading = manifest.status === "installing" || manifest.status === "uninstalling" || manifest.status === "updating" - const isInstalled = manifest.status === "installed" - const isFailed = manifest.status === "failed" - - const onClickUpdate = () => { - ipc.exec("bundle:update", manifest.id) - } - - const onClickPlay = () => { - ipc.exec("bundle:exec", manifest.id) - } - - const onClickFolder = () => { - ipc.exec("bundle:open", manifest.id) - } - - const onClickDelete = () => { - ipc.exec("bundle:uninstall", manifest.id) - } - - function handleUpdate(event, data) { - setManifest({ - ...manifest, - ...data, - }) - } - - function renderStatusLine(manifest) { - if (isLoading) { - return manifest.status - } - - return `v${manifest.version}` ?? "N/A" - } - - React.useEffect(() => { - ipc.on(`installation:${manifest.id}:status`, handleUpdate) - - return () => { - ipc.off(`installation:${manifest.id}:status`, handleUpdate) - } - }, []) - - React.useEffect(() => { - setManifest(props.manifest) - }, [props.manifest]) - - return
-
- - -
-

- { - manifest.pack_name - } -

-

- { - renderStatusLine(manifest) - } -

-
- -
- { - isInstalled && manifest.executable && } - onClick={onClickPlay} - /> - } - - { - isFailed && - Retry - - } - - { - isInstalled && } - onClick={onClickUpdate} - /> - } - - { - isInstalled && } - onClick={onClickFolder} - /> - } - - { - isInstalled && - } - /> - - } -
-
- -
- { - isLoading && - } - -

{manifest.statusText ?? "Unknown status"}

-
-
-} - class InstallationsManager extends React.Component { static contextType = InstallationsContext @@ -205,7 +44,7 @@ class InstallationsManager extends React.Component { { installations.map((manifest) => { - return + return }) } diff --git a/src/renderer/src/pages/manager/index.less b/src/renderer/src/pages/manager/index.less index 2029dde..73e2956 100644 --- a/src/renderer/src/pages/manager/index.less +++ b/src/renderer/src/pages/manager/index.less @@ -23,135 +23,4 @@ justify-content: center; } } -} - -@installation-item-borderRadius: 12px; - -.installation_item_wrapper { - position: relative; - - display: flex; - flex-direction: column; - - &.status_visible { - .installation_item { - border-bottom: 1px solid var(--border-color); - } - - .installation_status { - height: fit-content; - - padding: 10px 20px; - padding-top: calc(8px + 10px); - - opacity: 1; - transform: translateY(-8px); - } - } - - &:nth-child(odd) { - .installation_item { - background-color: var(--background-color-primary); - } - - .installation_status { - background-color: var(--background-color-primary); - } - } - - .installation_item { - display: flex; - flex-direction: row; - - gap: 20px; - - padding: 5px; - - border-radius: @installation-item-borderRadius; - - background-color: var(--background-color-primary); - - z-index: 50; - - .installation_item_info { - display: flex; - flex-direction: column; - - gap: 10px; - - p { - font-size: 0.7rem; - text-transform: uppercase; - } - } - - .installation_item_icon { - width: 50px; - height: 50px; - - min-width: 50px; - min-height: 50px; - - overflow: hidden; - - border-radius: 12px; - - img { - width: 100%; - height: 100%; - } - } - - .installation_item_actions { - display: flex; - - width: 100%; - - gap: 10px; - - align-items: center; - justify-content: flex-end; - } - } - - .installation_status { - position: relative; - - z-index: 49; - - display: inline-flex; - flex-direction: column; - - background-color: var(--background-color-primary); - - gap: 10px; - - width: 100%; - - border-radius: 0 0 12px 12px; - - padding: 0; - margin: 0; - opacity: 0; - height: 0; - overflow: hidden; - - p { - font-size: 0.7rem; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - line-height: 14px; - } - } -} - -.new_installation_prompt { - display: flex; - flex-direction: column; - - align-items: center; - justify-content: center; - - gap: 20px; } \ No newline at end of file diff --git a/src/renderer/src/style/fix.less b/src/renderer/src/style/fix.less new file mode 100644 index 0000000..3379168 --- /dev/null +++ b/src/renderer/src/style/fix.less @@ -0,0 +1,181 @@ +.ant-select-dropdown { + .ant-select-item-option:not(.ant-select-item-option-disabled) { + background-color: var(--background-color-primary) !important; + color: var(--text-color) !important; + } + + .ant-select-item-option-selected:not(.ant-select-item-option-disabled) { + background-color: var(--background-color-secondary) !important; + color: var(--text-color) !important; + } +} + +.ant-dropdown-menu { + .ant-dropdown-menu-item-divider { + background-color: var(--background-color-primary) !important; + } + + .ant-dropdown-menu-item-disabled { + opacity: 0.4; + + svg { + color: var(--text-color); + } + } +} + +.ant-modal-content { + background-color: var(--background-color-primary) !important; + + p, + span, + svg { + color: var(--text-color) !important; + } + + .ant-modal-confirm-content { + color: var(--text-color) !important; + } + + .ant-btn { + display: inline-flex; + align-items: center; + justify-content: center; + + svg { + margin: 0 !important; + } + + gap: 10px; + + span { + margin: 0; + line-height: 1rem; + } + } + + .ant-btn-primary:not(.ant-btn-dangerous) { + svg { + color: var(--background-color-primary) !important; + } + + span { + color: var(--background-color-primary) !important; + margin: 0; + } + + background-color: var(--primary-color); + + &:hover { + background-color: var(--primary-color); + } + } + + .ant-btn-primary:not(:disabled):not(.ant-btn-disabled) { + &:hover { + background-color: var(--primary-color); + filter: brightness(130%); + } + } + + .ant-btn-primary[disabled] { + opacity: 0.5 !important; + + svg { + color: unset !important; + } + + span { + color: unset !important; + margin: 0; + } + } + + .ant-btn[disabled] { + opacity: 0.5 !important; + } + + .ant-btn-default { + &:not(.ant-btn-dangerous) { + border-color: var(--background-color-secondary) !important; + + &:hover { + border-color: var(--background-color-primary) !important; + } + + &:active { + border-color: var(--background-color-primary) !important; + } + + &:focus { + border-color: var(--background-color-primary) !important; + } + } + + color: var(--text-color); + background: var(--background-color-primary); + + &:hover { + color: var(--text-color); + background: var(--background-color-secondary); + } + + &:active { + color: var(--text-color); + background: var(--background-color-secondary); + } + + &:focus { + color: var(--text-color); + background: var(--background-color-secondary); + } + } + + .ant-btn-dangerous { + span { + color: var(--ant-error-color); + } + } + + .ant-btn[disabled], + .ant-btn[disabled]:hover, + .ant-btn[disabled]:focus, + .ant-btn[disabled]:active { + background-color: var(--background-color-accent); + } +} + +.ant-notification-notice { + background-color: var(--background-color-primary) !important; + + h1, + h2, + h3, + h4, + h5, + h6, + p, + span { + color: var(--text-color) !important; + } + + .ant-notification-notice-message, + .ant-notification-notice-description { + color: var(--text-color) !important; + } +} + +.ant-message-notice-content { + background-color: var(--background-color-primary) !important; + + h1, + h2, + h3, + h4, + h5, + h6, + p, + span { + color: var(--text-color) !important; + } +} \ No newline at end of file diff --git a/src/renderer/src/style/index.less b/src/renderer/src/style/index.less index abc0a83..7bac6b2 100644 --- a/src/renderer/src/style/index.less +++ b/src/renderer/src/style/index.less @@ -1,4 +1,5 @@ @import "style/reset.css"; +@import "style/fix.less"; @var-text-color: #fff; @var-background-color-primary: #424549; @@ -9,6 +10,7 @@ :root { --background-color-primary: @var-background-color-primary; --background-color-secondary: @var-background-color-secondary; + --primary-color: @var-primary-color; --text-color: @var-text-color; --border-color: @var-border-color; @@ -78,7 +80,7 @@ body { .menu { display: flex; - flex-direction: row; + flex-direction: row; width: 100%;