Skip to content

Commit

Permalink
feat: Update the unbundling cluster confirmation box (kubesphere#3672)
Browse files Browse the repository at this point in the history
fix: Update the unbundling cluster confirmation box

Signed-off-by: TheYoungManLi <[email protected]>

Signed-off-by: TheYoungManLi <[email protected]>
  • Loading branch information
weili520 authored Sep 7, 2022
1 parent 1f24091 commit 821f856
Show file tree
Hide file tree
Showing 8 changed files with 508 additions and 2 deletions.
6 changes: 6 additions & 0 deletions locales/en/l10n-clusterManagement-clusters.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,10 @@ module.exports = {
LAST_KUBE_CONFIG_EXPIRED: 'KubeConfig expires in <span class="kubeConfig_expired">{count}</span> days',
VALIDATION_FAILED: 'Validation failed.',
NO_CLUSTER_TIP_DESC: 'A cluster is a group of nodes (physical or virtual machines) running KubeSphere.',
// List > Unbind Cluster
RISK_ALERT: 'Risk alert',
UNBIND_tIP_A: 'When a cluster is removed, the resources and configuration information in the original member cluster are not automatically cleared.',
UNBIND_tIP_B: 'See the <a href="https://kubesphere.io/docs/">official KubeSphere documentation</a> to manually clear the configuration information in the original member cluster to avoid resource conflicts when the original member cluster joins other multi-cluster systems.',
CLUSTER_CONFIRM_TEXT:'I have confirmed the cluster to be unbundled and know the risks, slide to continue',
INPUT_CLUSTER_NAME:'This action cannot be undone, please enter the cluster name {name} to confirm your removal of this cluster.'
}
5 changes: 3 additions & 2 deletions src/actions/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { IMPORT_CLUSTER } from 'configs/steps/clusters'
import { IMPORT_CLUSTER_SPEC } from 'components/Forms/Cluster/constants'
import KubeKeyClusterStore from 'stores/cluster/kubekey'
import { safeParseJSON } from 'utils'
import DeleteModal from 'components/Modals/Delete'
import UnbindCluster from 'pages/clusters/components/Modals/UnbindCluster'
import KubeConfigModal from 'components/Forms/Cluster/KubeConfig'

export default {
Expand Down Expand Up @@ -75,7 +75,8 @@ export default {
})
},
store,
modal: DeleteModal,
modal: UnbindCluster,
detail,
title: t('UNBIND_CLUSTER'),
resource: detail.name,
deleteCluster: true,
Expand Down
3 changes: 3 additions & 0 deletions src/assets/ksLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/assets/unbindCluster.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React, { useState, useRef, useEffect } from 'react'
import styles from './index.scss'

const Slider = props => {
const {
width = 560,
height = 40,
borderRadius = 40,
tipText,
handleSuccess = () => {},
} = props

const mouseDown = useRef(false)
const startPosition = useRef(0)
const mousePosition = useRef(0)
const maxMoveLength = useRef(width - 56)
const isSuccess = useRef(false)
const [isMouseEnter, setMouseEnter] = useState(false)
const [diff, setDiff] = useState(0)

const renderIcon = () => {
return <img className={styles.icon} src="/assets/ksLogo.svg" />
}

const handleMouseEnter = () => {
if (isSuccess.current || mouseDown.current) {
return
}
setMouseEnter(true)
setDiff(0)
}

const handleMouseLeave = () => {
if (isSuccess.current || mouseDown.current) {
return
}
setMouseEnter(false)
}

const handleMouseDown = e => {
if (isSuccess.current) {
return
}
startPosition.current = e.nativeEvent.x || e.touches[0].clientX
mouseDown.current = true
}

const handleMouseUp = () => {
if (isSuccess.current) {
return
}
mouseDown.current = false
setMouseEnter(true)
setDiff(0)
}

const handleMouseMove = e => {
if (!mouseDown.current || isSuccess.current) {
return
}
e.preventDefault()
e.stopPropagation()
mousePosition.current = e.x
let _diff = mousePosition.current - startPosition.current
if (_diff < 0) {
_diff = 0
}

if (_diff >= maxMoveLength.current) {
_diff = maxMoveLength.current
setDiff(_diff)
isSuccess.current = true
handleSuccess && handleSuccess()
return
}

setDiff(_diff)
}

useEffect(() => {
document.body.addEventListener('mouseup', handleMouseUp)
document.body.addEventListener('mousemove', handleMouseMove)

return () => {
document.body.removeEventListener('mouseup', handleMouseUp)
document.body.removeEventListener('mousemove', handleMouseMove)
}
}, [])

const sliderBoxStyle = {
borderRadius,
opacity: isMouseEnter && diff / maxMoveLength.current,
width: 56 + diff,
transitionDuration: !isMouseEnter || !mouseDown.current ? '.3s' : '0s',
}

const sliderStyle = {
borderRadius,
height: height - 8,
transitionDuration: !isMouseEnter || !mouseDown.current ? '.3s' : '0s',
transform: `translateX(${diff}px)`,
}

const tipStyle = {
color: `rgb(${121 + (255 - 121) * (diff / maxMoveLength.current)},
${135 + (255 - 135) * (diff / maxMoveLength.current)},
${156 + (255 - 156) * (diff / maxMoveLength.current)})`,
}

return (
<div className={styles.content} style={{ width }}>
<div className={styles.background} style={{ borderRadius }}>
<span style={tipStyle}>{tipText}</span>
</div>
<div className={styles.sliderBox} style={sliderBoxStyle}></div>
<div
style={sliderStyle}
className={styles.slider}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseMove={handleMouseMove}
>
{renderIcon()}
</div>
</div>
)
}

export default Slider
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.content {
margin-top: 12px;
position: relative;
z-index: 0;

.background {
height: 40px;
font-weight: 600;
font-size: 12px;
line-height: 20px;
display: flex;
justify-content: center;
align-items: center;
background: #eff4f9;
user-select: none;

span {
display: block;
z-index: 10;
text-overflow: ellipsis;
white-space: nowrap;
}
}

.sliderBox {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
background: #55bc8a;
opacity: 0;
display: flex;
align-items: center;
padding: 0px 4px;
z-index: 5;
}
}

.slider {
position: absolute;
top: 4px;
left: 4px;
width: 48px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0px 4px 8px rgba(36, 46, 66, 0.06);
background: #ffffff;
z-index: 20;

&:hover {
cursor: pointer;
}
}

.icon {
width: 24px;
height: 24px;
}
117 changes: 117 additions & 0 deletions src/pages/clusters/components/Modals/UnbindCluster/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, { useState } from 'react'
import { toJS } from 'mobx'
import { Input, Button } from '@kube-design/components'
import { Modal } from 'components/Base'
import ClusterTitle from 'components/Clusters/ClusterTitle'
import { getLocalTime } from 'utils'

import styles from './index.scss'
import SliderConfirm from './SliderConfirm'

const UnbindClusterModal = props => {
const { visible, isSubmitting, onCancel, onOk, title } = props
const detail = toJS(props.detail)

const [showConfirm, setShowConfirm] = useState(false)
const [inputName, setInputName] = useState('')

const handleSuccess = () => {
setShowConfirm(true)
}

const handleInputChange = e => {
setInputName(e.target.value)
}

const handleOk = () => {
onOk()
}

return (
<Modal
width={600}
showIcon={true}
imageIcon={'/assets/unbindCluster.svg'}
headerClassName={styles.header}
bodyClassName={styles.body}
title={title}
visible={visible}
isSubmitting={isSubmitting}
onCancel={onCancel}
hideFooter
>
<div style={{ padding: '0px 20px' }}>
<div className={styles.tipBox}>
<p className={styles.title}>{t('RISK_ALERT')}</p>
<ul>
<li className={styles.des}>{t('UNBIND_tIP_A')}</li>
<li className={styles.des}>{t.html('UNBIND_tIP_B')}</li>
</ul>
</div>
<div className={styles.cluster}>
<div className={styles.header}>
<ClusterTitle cluster={detail} />
</div>
<div className={styles.info}>
<div className={styles.item}>
<span className={styles.label}>{t('NODE_COUNT')}:</span>
<span className={styles.value}>{detail.nodeCount}</span>
</div>
<div className={styles.item}>
<span className={styles.label}>{t('KUBERNETES_VERSION')}:</span>
<span className={styles.value}>{detail.kubernetesVersion}</span>
</div>
<div className={styles.item}>
<span className={styles.label}>{t('PROVIDER')}:</span>
<span className={styles.value}>{detail.provider}</span>
</div>
<div className={styles.item}>
<span className={styles.label}>{t('CREATION_TIME')}:</span>
<span className={styles.value}>
{getLocalTime(detail.createTime).format(`YYYY-MM-DD HH:mm:ss`)}
</span>
</div>
</div>
</div>
{!showConfirm && (
<div style={{ marginBottom: 20 }}>
<SliderConfirm
width={560}
height={40}
borderRadius={40}
tipText={t('CLUSTER_CONFIRM_TEXT')}
handleSuccess={handleSuccess}
/>
</div>
)}
{showConfirm && (
<div className={styles.confirmInput}>
<p>{t.html('INPUT_CLUSTER_NAME', { name: detail.name })}</p>
<Input
placeholder={detail.name}
autoFocus={true}
onChange={handleInputChange}
/>
</div>
)}
</div>
{showConfirm && (
<div className={styles.footer}>
<Button onClick={onCancel}>{t('CANCEL')}</Button>
<Button
type="danger"
loading={isSubmitting}
disabled={
isSubmitting || (detail ? inputName !== detail.name : false)
}
onClick={handleOk}
>
{t('DROP')}
</Button>
</div>
)}
</Modal>
)
}

export default UnbindClusterModal
Loading

0 comments on commit 821f856

Please sign in to comment.