diff options
| author | Haishan <[email protected]> | 2020-07-01 22:06:26 +0800 |
|---|---|---|
| committer | Haishan <[email protected]> | 2020-07-04 17:58:56 +0800 |
| commit | 32bed273c83f0593187110d2b08a0f9ec5a7efd7 (patch) | |
| tree | 0b47da752de3ee0d87945c1122b2cf9d3bf8043f /src/components/rules | |
| parent | 55e928a87f561ab927774834b50e099a0758522d (diff) | |
feat: support rule provider
Diffstat (limited to 'src/components/rules')
| -rw-r--r-- | src/components/rules/RuleProviderItem.module.css | 43 | ||||
| -rw-r--r-- | src/components/rules/RuleProviderItem.tsx | 71 | ||||
| -rw-r--r-- | src/components/rules/TextFilter.tsx | 18 |
3 files changed, 132 insertions, 0 deletions
diff --git a/src/components/rules/RuleProviderItem.module.css b/src/components/rules/RuleProviderItem.module.css new file mode 100644 index 0000000..f4f52c8 --- /dev/null +++ b/src/components/rules/RuleProviderItem.module.css @@ -0,0 +1,43 @@ +.RuleProviderItem { + display: grid; + grid-template-columns: 40px 1fr 46px; + height: 100%; +} + +.left { + display: inline-flex; + align-items: center; + color: var(--color-text-secondary); + opacity: 0.4; +} + +.middle { + display: grid; + grid-template-rows: 1fr auto auto; + align-items: center; +} + +.gray { + color: #777; +} + +.refreshButtonWrapper { + display: grid; + place-items: center; +} + +.rotate { + display: inline-flex; +} +.isRotating { + animation: rotating 3s infinite linear; +} + +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/src/components/rules/RuleProviderItem.tsx b/src/components/rules/RuleProviderItem.tsx new file mode 100644 index 0000000..3b6d93d --- /dev/null +++ b/src/components/rules/RuleProviderItem.tsx @@ -0,0 +1,71 @@ +import cx from 'clsx'; +import { formatDistance } from 'date-fns'; +import * as React from 'react'; +import { RotateCw } from 'react-feather'; +import { queryCache, useMutation } from 'react-query'; +import { refreshRuleProviderByName } from 'src/api/rule-provider'; +import Button from 'src/components/Button'; +import { SectionNameType } from 'src/components/shared/Basic'; +import { ClashAPIConfig } from 'src/types'; + +import s from './RuleProviderItem.module.css'; + +function useRefresh( + name: string, + apiConfig: ClashAPIConfig +): [(ev: React.MouseEvent<HTMLButtonElement>) => unknown, boolean] { + const [mutate, { isLoading }] = useMutation(refreshRuleProviderByName, { + onSuccess: () => { + queryCache.invalidateQueries('/providers/rules'); + }, + }); + + const onClickRefreshButton = (ev: React.MouseEvent<HTMLButtonElement>) => { + ev.preventDefault(); + mutate({ name, apiConfig }); + }; + + return [onClickRefreshButton, isLoading]; +} + +function RotatableRotateCw({ isRotating }: { isRotating: boolean }) { + const cls = cx(s.rotate, { + [s.isRotating]: isRotating, + }); + return ( + <span className={cls}> + <RotateCw width={16} /> + </span> + ); +} + +export function RuleProviderItem({ + idx, + name, + vehicleType, + behavior, + updatedAt, + ruleCount, + apiConfig, +}) { + const [onClickRefreshButton, isRefreshing] = useRefresh(name, apiConfig); + const timeAgo = formatDistance(new Date(updatedAt), new Date()); + return ( + <div className={s.RuleProviderItem}> + <span className={s.left}>{idx}</span> + <div className={s.middle}> + <SectionNameType name={name} type={`${vehicleType} / ${behavior}`} /> + <div className={s.gray}> + {ruleCount < 2 ? `${ruleCount} rule` : `${ruleCount} rules`} + </div> + <small className={s.gray}>Updated {timeAgo} ago</small> + </div> + <span className={s.refreshButtonWrapper}> + <Button onClick={onClickRefreshButton} disabled={isRefreshing}> + <RotatableRotateCw isRotating={isRefreshing} /> + </Button> + </span> + </div> + ); +} + diff --git a/src/components/rules/TextFilter.tsx b/src/components/rules/TextFilter.tsx new file mode 100644 index 0000000..a3cc29e --- /dev/null +++ b/src/components/rules/TextFilter.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { useTextInut } from 'src/hooks/useTextInput'; +import { ruleFilterText } from 'src/store/rules'; + +import shared from '../shared.module.css'; + +export function TextFilter() { + const [onChange, text] = useTextInut(ruleFilterText); + return ( + <input + className={shared.input} + type="text" + value={text} + onChange={onChange} + placeholder="Filter" + /> + ); +} |
