Color inputs

This commit is contained in:
Jan Zípek 2024-04-01 19:13:54 +02:00
parent 6579a3ba52
commit 9232a9a0d6
Signed by: kamen
GPG Key ID: A17882625B33AC31
9 changed files with 226 additions and 18 deletions

View File

@ -26,6 +26,7 @@
"vite-plugin-svgr": "^2.2.1"
},
"dependencies": {
"@uiw/react-color-colorful": "^2.1.1",
"preact": "^10.10.6",
"react-query": "^3.39.2",
"wouter": "^2.8.0-alpha.2"

View File

@ -39,48 +39,83 @@ textarea {
color: var(--input-appendix-fg-color);
background-color: var(--input-appendix-bg-color);
>span {
> span {
display: flex;
align-items: center;
padding: 0 0.3rem;
height: 100%;
}
>button {
> button {
height: 100%;
}
}
}
.color-input {
display: flex;
align-items: center;
.current-color {
width: 2rem;
height: 1.5rem;
border: 1px solid var(--input-border-color);
margin-right: 0.5rem;
}
.color-picker {
margin-top: -280px;
> .title {
display: flex;
justify-content: space-between;
align-items: center;
.close {
display: inline-flex;
padding: 0.5rem;
cursor: pointer;
font-size: 125%;
}
}
position: absolute;
background-color: var(--box-bg-color);
padding: 0.5rem;
border: 1px solid var(--input-border-color);
}
}
.checkbox-label {
display: inline-flex;
align-items: center;
}
.checkbox-label input[type="checkbox"] {
margin-top: 6px;
.checkbox-label input[type='checkbox'] {
margin-top: 3px;
margin-right: 5px;
}
.input.buttons {
.button-picker {
>button {
> button {
margin: 0.1rem;
}
}
}
.input.date-time {
>div {
> div {
display: flex;
align-items: center;
}
input[type="date"] {
input[type='date'] {
margin-right: 0.25rem;
flex: 1;
}
input[type="time"] {
input[type='time'] {
flex-grow: 0;
flex-shrink: 0;
}
@ -109,7 +144,7 @@ textarea {
color: var(--input-hint-color);
font-size: 90%;
margin-top: 0rem;
margin-bottom: 0.50rem;
margin-bottom: 0.5rem;
}
.control {

View File

@ -0,0 +1,55 @@
import { forwardRef, useState } from 'preact/compat'
import Colorful from '@uiw/react-color-colorful'
import { CancelIcon } from '@/icons'
type Props = {
value: string
onChange: (e: Event) => void
name: string
ref: (el: HTMLInputElement | null) => void
}
export const CssColorInput = forwardRef<HTMLInputElement, Props>(
({ name, value, ref, onChange }: Props) => {
const [showPicker, setShowPicker] = useState(false)
const handleChange = (e: Event) => {
onChange(e)
}
return (
<div className="color-input">
{showPicker && (
<div className="color-picker">
<div className="title">
<div className="value">{value}</div>
<div className="close" onClick={() => setShowPicker(false)}>
<CancelIcon />
</div>
</div>
<Colorful
color={value}
onChange={(color: { hex: string }) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange({ target: { name, value: color.hex } } as any)
}
disableAlpha
/>
</div>
)}
<div
className="current-color"
style={{ background: value }}
onClick={() => setShowPicker(true)}
/>
<input
ref={ref}
name={name}
type="text"
value={value}
onChange={handleChange}
/>
</div>
)
}
)

View File

@ -1,3 +1,4 @@
import { CssColorInput } from '@/components/CssColorInput'
import { FormCheckboxField } from '@/components/FormCheckboxField'
import { FormField } from '@/components/FormField'
import { DashboardMQTTButtonData } from '@/utils/dashboard/parseDashboard'
@ -46,7 +47,7 @@ export const MQTTButtonSettings = ({ value, onChange }: Props) => {
<option value="2">2</option>
</select>
</FormField>
<FormCheckboxField name="retain" label="Retain">
<FormCheckboxField label="Retain">
<input type="checkbox" {...register('retain')} />
</FormCheckboxField>
<FormField name="topic" label="Topic">
@ -56,12 +57,32 @@ export const MQTTButtonSettings = ({ value, onChange }: Props) => {
<textarea required {...register('message')} rows={4} />
</FormField>
<FormCheckboxField
name="stretchButton"
label="Stretch button to full size"
hint="Makes button cover the whole area of the box"
>
<input type="checkbox" {...register('stretchButton')} />
</FormCheckboxField>
<FormField
name="fontSize"
label="Font Size"
hint="Text size multiplier, default 1"
>
<input
type="number"
step="any"
{...register('fontSize', { type: 'number' })}
/>
</FormField>
<FormField
name="color"
label="Text Color"
hint="Input any CSS compatible value"
>
<CssColorInput {...register('textColor')} />
</FormField>
<FormField label="Background Color" hint="Input any CSS compatible value">
<CssColorInput {...register('backgroundColor')} />
</FormField>
</>
)
}

View File

@ -18,9 +18,17 @@ export const BoxMQTTButtonContent = ({ data, ...boxProps }: Props) => {
}
return (
<EditableBox {...boxProps} onRefresh={() => null} hiddenTitle>
<EditableBox {...boxProps} onRefresh={() => null} hiddenTitle hideRefresh>
<div className={cn('mqtt-button', data.stretchButton && 'apply-stretch')}>
<button onClick={onClick} disabled={pushMutation.isLoading}>
<button
onClick={onClick}
disabled={pushMutation.isLoading}
style={{
fontSize: data.fontSize ? `${data.fontSize * 100}%` : undefined,
background: data.backgroundColor,
color: data.textColor,
}}
>
{boxProps.box.title ?? 'BUTTON'}
</button>
</div>

View File

@ -21,12 +21,14 @@ export type EditableBoxProps = {
type EditableBoxPropsWithExtra = EditableBoxProps & {
hiddenTitle?: boolean
hideRefresh?: boolean
children: ComponentChild
onRefresh: () => void
}
export const EditableBox = ({
hiddenTitle,
hideRefresh,
box,
children,
onPosition,
@ -143,9 +145,11 @@ export const EditableBox = ({
{!hiddenTitle && <div className="name">{box.title || ''}</div>}
</div>
<div className="actions">
{!hideRefresh && (
<div className="action" onClick={onRefresh}>
<RefreshIcon />
</div>
)}
<div className="action" onClick={() => setEditing(true)}>
<SettingsIcon />
</div>

View File

@ -55,6 +55,9 @@ export type DashboardMQTTButtonData = {
message: string
topic: string
stretchButton?: boolean
fontSize?: number
textColor?: string
backgroundColor?: string
}
export const parseDashboard = (input: string) => {

View File

@ -44,7 +44,9 @@
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": {
"@/*": ["src/*"],
"@shared/*": ["../shared/src/*"]
"@shared/*": ["../shared/src/*"],
"react": ["./node_modules/preact/compat/"],
"react-dom": ["./node_modules/preact/compat/"]
}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */

View File

@ -803,6 +803,84 @@ __metadata:
languageName: node
linkType: hard
"@uiw/color-convert@npm:2.1.1":
version: 2.1.1
resolution: "@uiw/color-convert@npm:2.1.1"
peerDependencies:
"@babel/runtime": ">=7.19.0"
checksum: e7c526ab880416c244e04762a3652f5324d93134b558cf945fbaa74953ba8c5e2ce320e5adae88b28791a852978f8d00118623d8157d0be1b033eb1f3db352dc
languageName: node
linkType: hard
"@uiw/react-color-alpha@npm:2.1.1":
version: 2.1.1
resolution: "@uiw/react-color-alpha@npm:2.1.1"
dependencies:
"@uiw/color-convert": 2.1.1
"@uiw/react-drag-event-interactive": 2.1.1
peerDependencies:
"@babel/runtime": ">=7.19.0"
react: ">=16.9.0"
react-dom: ">=16.9.0"
checksum: 2bf6996e71c58d16ec19bcfdceeeb4ea987eff253369dcb75afe057fa3b6557cf03f0859b052f48c6f2417a9a5b9fd006b946ee5e82701addf969bf8d7b2bac0
languageName: node
linkType: hard
"@uiw/react-color-colorful@npm:^2.1.1":
version: 2.1.1
resolution: "@uiw/react-color-colorful@npm:2.1.1"
dependencies:
"@uiw/color-convert": 2.1.1
"@uiw/react-color-alpha": 2.1.1
"@uiw/react-color-hue": 2.1.1
"@uiw/react-color-saturation": 2.1.1
peerDependencies:
"@babel/runtime": ">=7.19.0"
react: ">=16.9.0"
react-dom: ">=16.9.0"
checksum: 7e2ba3eae5a262fe96cac95e870b966c27379f60f6502536c81b8d6ddb57023a3c9992917b6a547736787871c4dad7e050dd0d78937f2a693a2f13c10fa2551d
languageName: node
linkType: hard
"@uiw/react-color-hue@npm:2.1.1":
version: 2.1.1
resolution: "@uiw/react-color-hue@npm:2.1.1"
dependencies:
"@uiw/color-convert": 2.1.1
"@uiw/react-color-alpha": 2.1.1
peerDependencies:
"@babel/runtime": ">=7.19.0"
react: ">=16.9.0"
react-dom: ">=16.9.0"
checksum: 7e76bbde85019bc1358b70094542089d492f2bb8d295f51294ed5175accc31f4ccd8f448930b3a0437750bb82f1318cb6ec5630acde11c39dd1e03e9e4f4a51f
languageName: node
linkType: hard
"@uiw/react-color-saturation@npm:2.1.1":
version: 2.1.1
resolution: "@uiw/react-color-saturation@npm:2.1.1"
dependencies:
"@uiw/color-convert": 2.1.1
"@uiw/react-drag-event-interactive": 2.1.1
peerDependencies:
"@babel/runtime": ">=7.19.0"
react: ">=16.9.0"
react-dom: ">=16.9.0"
checksum: efef55093ca05a49efbac022efedfc935e925489e42bf1cbac3428ff4b6e85a6401b26a48cda7b960bc922b4bd0a64486386b31999a9eb6d466b458e771c7030
languageName: node
linkType: hard
"@uiw/react-drag-event-interactive@npm:2.1.1":
version: 2.1.1
resolution: "@uiw/react-drag-event-interactive@npm:2.1.1"
peerDependencies:
"@babel/runtime": ">=7.19.0"
react: ">=16.9.0"
react-dom: ">=16.9.0"
checksum: 93d82b2a9ed89d9456187a16ab42d37ac7017e959f7a62e3c15594d0f22e091fe5c1cf805698f853f35902298055992f1d915527b0f488f1fc8d1ef821e6141e
languageName: node
linkType: hard
"abbrev@npm:1":
version: 1.1.1
resolution: "abbrev@npm:1.1.1"
@ -1182,6 +1260,7 @@ __metadata:
"@types/plotly.js": ^2.29.2
"@typescript-eslint/eslint-plugin": ^5.7.0
"@typescript-eslint/parser": ^5.7.0
"@uiw/react-color-colorful": ^2.1.1
eslint: ^8.5.0
eslint-config-prettier: ^8.5.0
eslint-plugin-prettier: ^4.0.0