Color inputs
This commit is contained in:
parent
6579a3ba52
commit
9232a9a0d6
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ export type DashboardMQTTButtonData = {
|
|||
message: string
|
||||
topic: string
|
||||
stretchButton?: boolean
|
||||
fontSize?: number
|
||||
textColor?: string
|
||||
backgroundColor?: string
|
||||
}
|
||||
|
||||
export const parseDashboard = (input: string) => {
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue