WIP
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { XIcon } from '@heroicons/react/outline'
|
||||
import React, { ReactNode, useRef } from 'react'
|
||||
import { useClickAway, useKey } from 'react-use'
|
||||
import { useClickAway, useKey, useKeyPress, useKeyPressEvent } from 'react-use'
|
||||
import Button from './Button'
|
||||
|
||||
export interface ModalProps {
|
||||
@@ -19,8 +19,8 @@ export default function Modal(props: ModalProps) {
|
||||
onClose?.()
|
||||
})
|
||||
|
||||
useKey('Escape', onClose, {
|
||||
event: 'keydown',
|
||||
useKeyPressEvent('Escape', e => {
|
||||
onClose?.()
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -30,7 +30,7 @@ export default function Modal(props: ModalProps) {
|
||||
>
|
||||
<div ref={ref} className={`modal ${className}`}>
|
||||
<div className="modal-header">
|
||||
<h3>{title}</h3>
|
||||
<h2>{title}</h2>
|
||||
<Button icon={<XIcon />} onClick={onClose} />
|
||||
</div>
|
||||
{children}
|
||||
|
||||
12
lama_cleaner/app/src/components/shared/NumberInput.scss
Normal file
12
lama_cleaner/app/src/components/shared/NumberInput.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
.number-input {
|
||||
all: unset;
|
||||
flex: 1 0 auto;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.2rem 0.8rem;
|
||||
line-height: 1;
|
||||
outline: 1px solid var(--border-color);
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid var(--yellow-accent);
|
||||
}
|
||||
}
|
||||
31
lama_cleaner/app/src/components/shared/NumberInput.tsx
Normal file
31
lama_cleaner/app/src/components/shared/NumberInput.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React, { FormEvent, InputHTMLAttributes } from 'react'
|
||||
|
||||
interface NumberInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
value: string
|
||||
onValue?: (val: string) => void
|
||||
}
|
||||
|
||||
const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
||||
(props: NumberInputProps, forwardedRef) => {
|
||||
const { value, onValue, ...itemProps } = props
|
||||
|
||||
const handleOnInput = (evt: FormEvent<HTMLInputElement>) => {
|
||||
const target = evt.target as HTMLInputElement
|
||||
const val = target.value.replace(/\D/g, '')
|
||||
onValue?.(val)
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
value={value}
|
||||
onInput={handleOnInput}
|
||||
className="number-input"
|
||||
{...itemProps}
|
||||
ref={forwardedRef}
|
||||
type="text"
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default NumberInput
|
||||
74
lama_cleaner/app/src/components/shared/Selector.scss
Normal file
74
lama_cleaner/app/src/components/shared/Selector.scss
Normal file
@@ -0,0 +1,74 @@
|
||||
@use '../../styles/Mixins' as *;
|
||||
|
||||
.selector {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.selector-main {
|
||||
@include accented-display(var(white));
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
gap: 8px;
|
||||
padding: 0.2rem 0.8rem;
|
||||
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--options-text-color);
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-options {
|
||||
@include accented-display(var(--btn-primary-bg));
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
justify-self: center;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 3rem;
|
||||
|
||||
color: var(--options-text-color);
|
||||
background-color: var(--page-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
border-radius: 0.6rem;
|
||||
|
||||
@include mobile {
|
||||
bottom: 11.5rem;
|
||||
}
|
||||
|
||||
.selector-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
padding: 0.5rem 0.8rem;
|
||||
|
||||
&:first-of-type {
|
||||
border-top-right-radius: 0.5rem;
|
||||
border-top-left-radius: 0.5rem;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
border-bottom-right-radius: 0.5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--yellow-accent);
|
||||
color: var(--btn-text-hover-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
lama_cleaner/app/src/components/shared/Selector.tsx
Normal file
87
lama_cleaner/app/src/components/shared/Selector.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import React, { MutableRefObject, useCallback, useRef, useState } from 'react'
|
||||
import { useClickAway, useKeyPressEvent } from 'react-use'
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/outline'
|
||||
|
||||
type SelectorChevronDirection = 'up' | 'down'
|
||||
|
||||
type SelectorProps = {
|
||||
minWidth?: number
|
||||
chevronDirection?: SelectorChevronDirection
|
||||
options: string[]
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const selectorDefaultProps = {
|
||||
minWidth: 128,
|
||||
chevronDirection: 'down',
|
||||
}
|
||||
|
||||
function Selector(props: SelectorProps) {
|
||||
const { minWidth, chevronDirection, options, onChange } = props
|
||||
const [showOptions, setShowOptions] = useState<boolean>(false)
|
||||
const [index, setIndex] = useState<number>(0)
|
||||
const selectorRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const showOptionsHandler = () => {
|
||||
// console.log(selectorRef.current?.focus)
|
||||
// selectorRef?.current?.focus()
|
||||
setShowOptions(currentShowOptionsState => !currentShowOptionsState)
|
||||
}
|
||||
|
||||
useClickAway(selectorRef, () => {
|
||||
setShowOptions(false)
|
||||
})
|
||||
|
||||
// TODO: how to prevent Modal close?
|
||||
// useKeyPressEvent('Escape', (e: KeyboardEvent) => {
|
||||
// if (showOptions === true) {
|
||||
// console.log(`selector ${e}`)
|
||||
// e.preventDefault()
|
||||
// e.stopPropagation()
|
||||
// setShowOptions(false)
|
||||
// }
|
||||
// })
|
||||
|
||||
const onOptionClick = (e: any, newIndex: number) => {
|
||||
const currentRes = e.target.textContent.split('x')
|
||||
onChange(currentRes[0])
|
||||
setShowOptions(false)
|
||||
setIndex(newIndex)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="selector" ref={selectorRef} style={{ minWidth }}>
|
||||
<div
|
||||
className="selector-main"
|
||||
role="button"
|
||||
onClick={showOptionsHandler}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<p>{options[index]}</p>
|
||||
<div className="selector-icon">
|
||||
{chevronDirection === 'up' ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showOptions && (
|
||||
<div className="selector-options">
|
||||
{options.map((val, _index) => (
|
||||
<div
|
||||
className="selector-option"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
key={val}
|
||||
onClick={e => onOptionClick(e, _index)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{val}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Selector.defaultProps = selectorDefaultProps
|
||||
export default Selector
|
||||
36
lama_cleaner/app/src/components/shared/Switch.scss
Normal file
36
lama_cleaner/app/src/components/shared/Switch.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
.switch-root {
|
||||
all: 'unset';
|
||||
width: 42px;
|
||||
height: 25px;
|
||||
background-color: var(--switch-root-background-color);
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
position: relative;
|
||||
transition: background-color 100ms;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-root[data-state='checked'] {
|
||||
background-color: var(--yellow-accent);
|
||||
}
|
||||
|
||||
.switch-thumb {
|
||||
display: block;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
background-color: var(--switch-thumb-color);
|
||||
border-radius: 9999px;
|
||||
transition: transform 100ms;
|
||||
transform: translateX(4px);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.switch-thumb[data-state='checked'] {
|
||||
transform: translateX(21px);
|
||||
background-color: var(--switch-thumb-checked-color);
|
||||
outline: 1px solid rgb(100, 100, 120, 0.5);
|
||||
}
|
||||
34
lama_cleaner/app/src/components/shared/Switch.tsx
Normal file
34
lama_cleaner/app/src/components/shared/Switch.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import * as SwitchPrimitive from '@radix-ui/react-switch'
|
||||
|
||||
const Switch = React.forwardRef<
|
||||
React.ElementRef<typeof SwitchPrimitive.Root>,
|
||||
React.ComponentProps<typeof SwitchPrimitive.Root>
|
||||
>((props, forwardedRef) => {
|
||||
const { className, ...itemProps } = props
|
||||
|
||||
return (
|
||||
<SwitchPrimitive.Root
|
||||
{...itemProps}
|
||||
ref={forwardedRef}
|
||||
className={`switch-root ${className}`}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const SwitchThumb = React.forwardRef<
|
||||
React.ElementRef<typeof SwitchPrimitive.Thumb>,
|
||||
React.ComponentProps<typeof SwitchPrimitive.Thumb>
|
||||
>((props, forwardedRef) => {
|
||||
const { className, ...itemProps } = props
|
||||
|
||||
return (
|
||||
<SwitchPrimitive.Thumb
|
||||
{...itemProps}
|
||||
ref={forwardedRef}
|
||||
className={`switch-thumb ${className}`}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export { Switch, SwitchThumb }
|
||||
Reference in New Issue
Block a user