Initial commit
This commit is contained in:
3
web/app/components/develop/secret-key/assets/copied.svg
Normal file
3
web/app/components/develop/secret-key/assets/copied.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6665 2.66683C11.2865 2.66683 11.5965 2.66683 11.8508 2.73498C12.541 2.91991 13.0801 3.45901 13.265 4.14919C13.3332 4.40352 13.3332 4.71352 13.3332 5.3335V11.4668C13.3332 12.5869 13.3332 13.147 13.1152 13.5748C12.9234 13.9511 12.6175 14.2571 12.2412 14.4488C11.8133 14.6668 11.2533 14.6668 10.1332 14.6668H5.8665C4.7464 14.6668 4.18635 14.6668 3.75852 14.4488C3.3822 14.2571 3.07624 13.9511 2.88449 13.5748C2.6665 13.147 2.6665 12.5869 2.6665 11.4668V5.3335C2.6665 4.71352 2.6665 4.40352 2.73465 4.14919C2.91959 3.45901 3.45868 2.91991 4.14887 2.73498C4.4032 2.66683 4.71319 2.66683 5.33317 2.66683M5.99984 10.0002L7.33317 11.3335L10.3332 8.3335M6.39984 4.00016H9.59984C9.9732 4.00016 10.1599 4.00016 10.3025 3.9275C10.4279 3.86359 10.5299 3.7616 10.5938 3.63616C10.6665 3.49355 10.6665 3.30686 10.6665 2.9335V2.40016C10.6665 2.02679 10.6665 1.84011 10.5938 1.6975C10.5299 1.57206 10.4279 1.47007 10.3025 1.40616C10.1599 1.3335 9.97321 1.3335 9.59984 1.3335H6.39984C6.02647 1.3335 5.83978 1.3335 5.69718 1.40616C5.57174 1.47007 5.46975 1.57206 5.40583 1.6975C5.33317 1.84011 5.33317 2.02679 5.33317 2.40016V2.9335C5.33317 3.30686 5.33317 3.49355 5.40583 3.63616C5.46975 3.7616 5.57174 3.86359 5.69718 3.9275C5.83978 4.00016 6.02647 4.00016 6.39984 4.00016Z" stroke="#1D2939" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6665 2.66634H11.9998C12.3535 2.66634 12.6926 2.80682 12.9426 3.05687C13.1927 3.30691 13.3332 3.64605 13.3332 3.99967V13.333C13.3332 13.6866 13.1927 14.0258 12.9426 14.2758C12.6926 14.5259 12.3535 14.6663 11.9998 14.6663H3.99984C3.64622 14.6663 3.30708 14.5259 3.05703 14.2758C2.80698 14.0258 2.6665 13.6866 2.6665 13.333V3.99967C2.6665 3.64605 2.80698 3.30691 3.05703 3.05687C3.30708 2.80682 3.64622 2.66634 3.99984 2.66634H5.33317M5.99984 1.33301H9.99984C10.368 1.33301 10.6665 1.63148 10.6665 1.99967V3.33301C10.6665 3.7012 10.368 3.99967 9.99984 3.99967H5.99984C5.63165 3.99967 5.33317 3.7012 5.33317 3.33301V1.99967C5.33317 1.63148 5.63165 1.33301 5.99984 1.33301Z" stroke="#1D2939" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 875 B |
3
web/app/components/develop/secret-key/assets/copy.svg
Normal file
3
web/app/components/develop/secret-key/assets/copy.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6665 2.66634H11.9998C12.3535 2.66634 12.6926 2.80682 12.9426 3.05687C13.1927 3.30691 13.3332 3.64605 13.3332 3.99967V13.333C13.3332 13.6866 13.1927 14.0258 12.9426 14.2758C12.6926 14.5259 12.3535 14.6663 11.9998 14.6663H3.99984C3.64622 14.6663 3.30708 14.5259 3.05703 14.2758C2.80698 14.0258 2.6665 13.6866 2.6665 13.333V3.99967C2.6665 3.64605 2.80698 3.30691 3.05703 3.05687C3.30708 2.80682 3.64622 2.66634 3.99984 2.66634H5.33317M5.99984 1.33301H9.99984C10.368 1.33301 10.6665 1.63148 10.6665 1.99967V3.33301C10.6665 3.7012 10.368 3.99967 9.99984 3.99967H5.99984C5.63165 3.99967 5.33317 3.7012 5.33317 3.33301V1.99967C5.33317 1.63148 5.63165 1.33301 5.99984 1.33301Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 875 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 2H10M2 4H14M12.6667 4L12.1991 11.0129C12.129 12.065 12.0939 12.5911 11.8667 12.99C11.6666 13.3412 11.3648 13.6235 11.0011 13.7998C10.588 14 10.0607 14 9.00623 14H6.99377C5.93927 14 5.41202 14 4.99889 13.7998C4.63517 13.6235 4.33339 13.3412 4.13332 12.99C3.90607 12.5911 3.871 12.065 3.80086 11.0129L3.33333 4M6.66667 7V10.3333M9.33333 7V10.3333" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 548 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 2H10M2 4H14M12.6667 4L12.1991 11.0129C12.129 12.065 12.0939 12.5911 11.8667 12.99C11.6666 13.3412 11.3648 13.6235 11.0011 13.7998C10.588 14 10.0607 14 9.00623 14H6.99377C5.93927 14 5.41202 14 4.99889 13.7998C4.63517 13.6235 4.33339 13.3412 4.13332 12.99C3.90607 12.5911 3.871 12.065 3.80086 11.0129L3.33333 4M6.66667 7V10.3333M9.33333 7V10.3333" stroke="#D92D20" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 550 B |
71
web/app/components/develop/secret-key/input-copy.tsx
Normal file
71
web/app/components/develop/secret-key/input-copy.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { t } from 'i18next'
|
||||
import s from './style.module.css'
|
||||
|
||||
type IInputCopyProps = {
|
||||
value?: string
|
||||
className?: string
|
||||
readOnly?: boolean
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const InputCopy = ({
|
||||
value,
|
||||
className,
|
||||
readOnly = true,
|
||||
children,
|
||||
}: IInputCopyProps) => {
|
||||
const [_, copy] = useCopyToClipboard()
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const timeout = setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}, [isCopied])
|
||||
|
||||
return (
|
||||
<div className={`flex rounded-lg bg-gray-50 hover:bg-gray-50 py-2 items-center ${className}`}>
|
||||
<div className="flex items-center flex-grow h-5">
|
||||
{children}
|
||||
<div className='flex-grow bg-gray-50 text-[13px] relative h-full'>
|
||||
<Tooltip
|
||||
selector="top-uniq"
|
||||
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
className='z-10'
|
||||
>
|
||||
<div className='absolute top-0 left-0 w-full pl-2 pr-2 truncate cursor-pointer r-0' onClick={() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
}}>{value}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex-shrink-0 h-4 bg-gray-200 border" />
|
||||
<Tooltip
|
||||
selector="top-uniq"
|
||||
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
className='z-10'
|
||||
>
|
||||
<div className="px-0.5 flex-shrink-0">
|
||||
<div className={`box-border w-[30px] h-[30px] flex items-center justify-center rounded-lg hover:bg-gray-100 cursor-pointer ${s.copyIcon} ${isCopied ? s.copied : ''}`} onClick={() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
}}>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InputCopy
|
31
web/app/components/develop/secret-key/secret-key-button.tsx
Normal file
31
web/app/components/develop/secret-key/secret-key-button.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
|
||||
// import { KeyIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
type ISecretKeyButtonProps = {
|
||||
className?: string
|
||||
appId: string
|
||||
}
|
||||
|
||||
const SecretKeyButton = ({ className, appId }: ISecretKeyButtonProps) => {
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
<Button className={`px-3 ${className}`} type='default' onClick={() => setVisible(true)}>
|
||||
<div className='flex items-center justify-center w-4 h-4 mr-2'>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 3.66672C9.35362 3.66672 9.69276 3.80719 9.94281 4.05724C10.1929 4.30729 10.3333 4.64643 10.3333 5.00005M13 5.00005C13.0002 5.62483 12.854 6.24097 12.5732 6.79908C12.2924 7.3572 11.8847 7.84177 11.3829 8.21397C10.8811 8.58617 10.2991 8.83564 9.68347 8.94239C9.06788 9.04915 8.43584 9.01022 7.838 8.82872L6.33333 10.3334H5V11.6667H3.66667V13.0001H1.66667C1.48986 13.0001 1.32029 12.9298 1.19526 12.8048C1.07024 12.6798 1 12.5102 1 12.3334V10.6094C1.00004 10.4326 1.0703 10.263 1.19533 10.1381L5.17133 6.16205C5.00497 5.61206 4.95904 5.03268 5.0367 4.46335C5.11435 3.89402 5.31375 3.3481 5.62133 2.86275C5.92891 2.3774 6.33744 1.96401 6.81913 1.65073C7.30082 1.33745 7.84434 1.13162 8.41272 1.04725C8.9811 0.96289 9.56098 1.00197 10.1129 1.16184C10.6648 1.32171 11.1758 1.59861 11.6111 1.97369C12.0464 2.34878 12.3958 2.81324 12.6354 3.33548C12.8751 3.85771 12.9994 4.42545 13 5.00005Z" stroke="#1F2A37" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className='text-[13px] text-gray-800'>{t('appApi.apiKey')}</div>
|
||||
</Button>
|
||||
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretKeyButton
|
@@ -0,0 +1,41 @@
|
||||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import InputCopy from './input-copy'
|
||||
import s from './style.module.css'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import type { CreateApiKeyResponse } from '@/models/app'
|
||||
|
||||
type ISecretKeyGenerateModalProps = {
|
||||
isShow: boolean
|
||||
onClose: () => void
|
||||
newKey?: CreateApiKeyResponse
|
||||
className?: string
|
||||
}
|
||||
|
||||
const SecretKeyGenerateModal = ({
|
||||
isShow = false,
|
||||
onClose,
|
||||
newKey,
|
||||
className
|
||||
}: ISecretKeyGenerateModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`px-8 ${className}`}>
|
||||
<XMarkIcon className={`w-6 h-6 absolute cursor-pointer text-gray-500 ${s.close}`} onClick={onClose} />
|
||||
<p className='mt-1 text-[13px] text-gray-500 font-normal leading-5'>{t('appApi.apiKeyModal.generateTips')}</p>
|
||||
<div className='my-4'>
|
||||
<InputCopy className='w-full' value={newKey?.token} />
|
||||
</div>
|
||||
<div className='flex justify-end my-4'>
|
||||
<Button type='default' className={`flex-shrink-0 ${s.w64}`} onClick={onClose}>
|
||||
<span className='text-xs font-medium text-gray-800'>{t('appApi.actionMsg.ok')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</Modal >
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretKeyGenerateModal
|
165
web/app/components/develop/secret-key/secret-key-modal.tsx
Normal file
165
web/app/components/develop/secret-key/secret-key-modal.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
'use client'
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import useSWR, { useSWRConfig } from 'swr'
|
||||
import SecretKeyGenerateModal from './secret-key-generate'
|
||||
import s from './style.module.css'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { createApikey, delApikey, fetchApiKeysList } from '@/service/apps'
|
||||
import type { CreateApiKeyResponse } from '@/models/app'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import I18n from '@/context/i18n'
|
||||
|
||||
type ISecretKeyModalProps = {
|
||||
isShow: boolean
|
||||
appId: string
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const SecretKeyModal = ({
|
||||
isShow = false,
|
||||
appId,
|
||||
onClose,
|
||||
}: ISecretKeyModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const [newKey, setNewKey] = useState<CreateApiKeyResponse | undefined>(undefined)
|
||||
const { mutate } = useSWRConfig()
|
||||
const commonParams = { url: `/apps/${appId}/api-keys`, params: {} }
|
||||
const { data: apiKeysList } = useSWR(commonParams, fetchApiKeysList)
|
||||
|
||||
const [delKeyID, setDelKeyId] = useState('')
|
||||
const [_, copy] = useCopyToClipboard()
|
||||
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
// const [isCopied, setIsCopied] = useState(false)
|
||||
const [copyValue, setCopyValue] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (copyValue) {
|
||||
const timeout = setTimeout(() => {
|
||||
setCopyValue('')
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}, [copyValue])
|
||||
|
||||
|
||||
const onDel = async () => {
|
||||
setShowConfirmDelete(false)
|
||||
if (!delKeyID) {
|
||||
return
|
||||
}
|
||||
await delApikey({ url: `/apps/${appId}/api-keys/${delKeyID}`, params: {} })
|
||||
mutate(commonParams)
|
||||
}
|
||||
|
||||
const onCreate = async () => {
|
||||
const res = await createApikey({ url: `/apps/${appId}/api-keys`, body: {} })
|
||||
setVisible(true)
|
||||
setNewKey(res)
|
||||
mutate(commonParams)
|
||||
}
|
||||
|
||||
const generateToken = (token: string) => {
|
||||
return `${token.slice(0, 3)}...${token.slice(-20)}`
|
||||
}
|
||||
|
||||
const formatDate = (timestamp: any) => {
|
||||
if (locale === 'en') {
|
||||
return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000)
|
||||
} else {
|
||||
return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`${s.customModal} px-8 flex flex-col`}>
|
||||
<XMarkIcon className={`w-6 h-6 absolute cursor-pointer text-gray-500 ${s.close}`} onClick={onClose} />
|
||||
<p className='mt-1 text-[13px] text-gray-500 font-normal leading-5 flex-shrink-0'>{t('appApi.apiKeyModal.apiSecretKeyTips')}</p>
|
||||
{!apiKeysList && <div className='mt-4'><Loading /></div>}
|
||||
{
|
||||
!!apiKeysList?.data?.length && (
|
||||
<div className='flex flex-col flex-grow mt-4 overflow-hidden'>
|
||||
<div className='flex items-center flex-shrink-0 text-xs font-semibold text-gray-500 border-b border-solid h-9'>
|
||||
<div className='flex-shrink-0 w-64 px-3'>{t('appApi.apiKeyModal.secretKey')}</div>
|
||||
<div className='flex-shrink-0 px-3 w-28'>{t('appApi.apiKeyModal.created')}</div>
|
||||
<div className='flex-shrink-0 px-3 w-28'>{t('appApi.apiKeyModal.lastUsed')}</div>
|
||||
<div className='flex-grow px-3'></div>
|
||||
</div>
|
||||
<div className='flex-grow overflow-auto'>
|
||||
{apiKeysList.data.map(api => (
|
||||
<div className='flex items-center text-sm font-normal text-gray-700 border-b border-solid h-9' key={api.id}>
|
||||
<div className='flex-shrink-0 w-64 px-3 font-mono truncate'>{generateToken(api.token)}</div>
|
||||
<div className='flex-shrink-0 px-3 truncate w-28'>{formatDate(api.created_at)}</div>
|
||||
{/* <div className='flex-shrink-0 px-3 truncate w-28'>{dayjs((+api.created_at) * 1000).format('MMM D, YYYY')}</div> */}
|
||||
{/* <div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? dayjs((+api.last_used_at) * 1000).format('MMM D, YYYY') : 'Never'}</div> */}
|
||||
<div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? formatDate(api.last_used_at) : t('appApi.never')}</div>
|
||||
<div className='flex flex-grow px-3'>
|
||||
<Tooltip
|
||||
selector="top-uniq"
|
||||
content={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
className='z-10'
|
||||
>
|
||||
<div className={`flex items-center justify-center flex-shrink-0 w-6 h-6 mr-1 rounded-lg cursor-pointer hover:bg-gray-100 ${s.copyIcon} ${copyValue === api.token ? s.copied : ''}`} onClick={() => {
|
||||
// setIsCopied(true)
|
||||
copy(api.token)
|
||||
setCopyValue(api.token)
|
||||
}}></div>
|
||||
</Tooltip>
|
||||
<div className={`flex items-center justify-center flex-shrink-0 w-6 h-6 rounded-lg cursor-pointer ${s.trashIcon}`} onClick={() => {
|
||||
setDelKeyId(api.id)
|
||||
setShowConfirmDelete(true)
|
||||
}}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='flex'>
|
||||
<Button type='default' className={`flex flex-shrink-0 mt-4 ${s.autoWidth}`} onClick={() =>
|
||||
onCreate()
|
||||
}>
|
||||
<PlusIcon className='flex flex-shrink-0 w-4 h-4' />
|
||||
<div className='text-xs font-medium text-gray-800'>{t('appApi.apiKeyModal.createNewSecretKey')}</div>
|
||||
</Button>
|
||||
</div>
|
||||
<SecretKeyGenerateModal className='flex-shrink-0' isShow={isVisible} onClose={() => setVisible(false)} newKey={newKey} />
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
title={`${t('appApi.actionMsg.deleteConfirmTitle')}`}
|
||||
content={`${t('appApi.actionMsg.deleteConfirmTips')}`}
|
||||
isShow={showConfirmDelete}
|
||||
onClose={() => {
|
||||
setDelKeyId('')
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
onConfirm={onDel}
|
||||
onCancel={() => {
|
||||
setDelKeyId('')
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Modal >
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretKeyModal
|
61
web/app/components/develop/secret-key/style.module.css
Normal file
61
web/app/components/develop/secret-key/style.module.css
Normal file
@@ -0,0 +1,61 @@
|
||||
.customModal {
|
||||
max-width: 40rem !important;
|
||||
max-height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
.close {
|
||||
top: 1.5rem;
|
||||
right: 1.5rem;
|
||||
}
|
||||
|
||||
.trash {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.w64 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
||||
.w320 {
|
||||
width: 20rem;
|
||||
}
|
||||
|
||||
.customApi {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.autoWidth {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.trashIcon {
|
||||
background-color: transparent;
|
||||
background-image: url(./assets/trash-gray.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.trashIcon:hover {
|
||||
background-color: rgba(254, 228, 226, 1);
|
||||
background-image: url(./assets/trash-red.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.copyIcon {
|
||||
background-image: url(./assets/copy.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyIcon:hover {
|
||||
background-image: url(./assets/copy-hover.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyIcon.copied {
|
||||
background-image: url(./assets/copied.svg);
|
||||
}
|
Reference in New Issue
Block a user