Merge branch 'asmodeus' of git.aiterp.net:lucifer/new-server into asmodeus
This commit is contained in:
@@ -12,6 +12,10 @@ func Bridges(r gin.IRoutes) {
|
|||||||
return config.BridgeRepository().FetchAll(ctxOf(c))
|
return config.BridgeRepository().FetchAll(ctxOf(c))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
r.GET("/:id", handler(func(c *gin.Context) (interface{}, error) {
|
||||||
|
return config.BridgeRepository().Find(ctxOf(c), intParam(c, "id"))
|
||||||
|
}))
|
||||||
|
|
||||||
r.POST("", handler(func(c *gin.Context) (interface{}, error) {
|
r.POST("", handler(func(c *gin.Context) (interface{}, error) {
|
||||||
var body struct {
|
var body struct {
|
||||||
Driver models.DriverKind `json:"driver"`
|
Driver models.DriverKind `json:"driver"`
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/lucifer/new-server/app/config"
|
||||||
|
"git.aiterp.net/lucifer/new-server/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ColorPresets(r gin.IRoutes) {
|
||||||
|
r.GET("", handler(func(c *gin.Context) (interface{}, error) {
|
||||||
|
return config.ColorPresetRepository().FetchAll(ctxOf(c))
|
||||||
|
}))
|
||||||
|
|
||||||
|
r.GET("/:id", handler(func(c *gin.Context) (interface{}, error) {
|
||||||
|
return config.ColorPresetRepository().Find(ctxOf(c), intParam(c, "id"))
|
||||||
|
}))
|
||||||
|
|
||||||
|
r.POST("", handler(func(c *gin.Context) (interface{}, error) {
|
||||||
|
var body struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ColorString string `json:"colorString"`
|
||||||
|
}
|
||||||
|
err := parseBody(c, &body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newColor, err := models.ParseColorValue(body.ColorString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
preset := models.ColorPreset{
|
||||||
|
Name: body.Name,
|
||||||
|
Value: newColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
preset.Validate()
|
||||||
|
err = config.ColorPresetRepository().Save(ctxOf(c), &preset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return preset, nil
|
||||||
|
}))
|
||||||
|
|
||||||
|
r.PUT("/:id", handler(func(c *gin.Context) (interface{}, error) {
|
||||||
|
var body struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
ColorString *string `json:"colorString"`
|
||||||
|
}
|
||||||
|
err := parseBody(c, &body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
preset, err := config.ColorPresetRepository().Find(ctxOf(c), intParam(c, "id"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if body.Name != nil {
|
||||||
|
preset.Name = *body.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if body.ColorString != nil {
|
||||||
|
newColor, err := models.ParseColorValue(*body.ColorString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
preset.Value = newColor
|
||||||
|
}
|
||||||
|
|
||||||
|
preset.Validate()
|
||||||
|
err = config.ColorPresetRepository().Save(ctxOf(c), &preset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return preset, nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
+13
-2
@@ -1,12 +1,23 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import "git.aiterp.net/lucifer/new-server/models"
|
import (
|
||||||
|
"git.aiterp.net/lucifer/new-server/internal/drivers"
|
||||||
|
"git.aiterp.net/lucifer/new-server/internal/drivers/nanoleaf"
|
||||||
|
"git.aiterp.net/lucifer/new-server/models"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
var dp models.DriverProvider
|
var dp models.DriverProvider
|
||||||
|
var dpMutex sync.Mutex
|
||||||
|
|
||||||
func DriverProvider() models.DriverProvider {
|
func DriverProvider() models.DriverProvider {
|
||||||
|
dpMutex.Lock()
|
||||||
|
defer dpMutex.Unlock()
|
||||||
|
|
||||||
if dp == nil {
|
if dp == nil {
|
||||||
panic("not implemented yet")
|
dp = drivers.DriverMap{
|
||||||
|
models.DTNanoLeaf: &nanoleaf.Driver{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dp
|
return dp
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
bRepo models.BridgeRepository
|
bRepo models.BridgeRepository
|
||||||
|
cpRepo models.ColorPresetRepository
|
||||||
dRepo models.DeviceRepository
|
dRepo models.DeviceRepository
|
||||||
ehRepo models.EventHandlerRepository
|
ehRepo models.EventHandlerRepository
|
||||||
)
|
)
|
||||||
@@ -19,6 +20,14 @@ func BridgeRepository() models.BridgeRepository {
|
|||||||
return bRepo
|
return bRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ColorPresetRepository() models.ColorPresetRepository {
|
||||||
|
if cpRepo == nil {
|
||||||
|
cpRepo = &mysql.ColorPresetRepo{DBX: DBX()}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpRepo
|
||||||
|
}
|
||||||
|
|
||||||
func DeviceRepository() models.DeviceRepository {
|
func DeviceRepository() models.DeviceRepository {
|
||||||
if dRepo == nil {
|
if dRepo == nil {
|
||||||
panic("panik")
|
panic("panik")
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func StartServer() {
|
|||||||
|
|
||||||
apiGin := ginny.Group("/api")
|
apiGin := ginny.Group("/api")
|
||||||
api.Bridges(apiGin.Group("/bridges"))
|
api.Bridges(apiGin.Group("/bridges"))
|
||||||
|
api.ColorPresets(apiGin.Group("/color-presets"))
|
||||||
api.DriverKinds(apiGin.Group("/driver-kinds"))
|
api.DriverKinds(apiGin.Group("/driver-kinds"))
|
||||||
api.Events(apiGin.Group("/events"))
|
api.Events(apiGin.Group("/events"))
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.aiterp.net/lucifer/new-server/internal/drivers/nanoleaf"
|
"git.aiterp.net/lucifer/new-server/app/config"
|
||||||
"git.aiterp.net/lucifer/new-server/models"
|
"git.aiterp.net/lucifer/new-server/models"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var flagDriver = flag.String("driver", "Nanoleaf", "The bridge driver to use")
|
var flagDriver = flag.String("driver", string(models.DTNanoLeaf), "The bridge driver to use")
|
||||||
var flagAddress = flag.String("address", "127.0.0.1", "The bridge's address")
|
var flagAddress = flag.String("address", "127.0.0.1", "The bridge's address")
|
||||||
var flagToken = flag.String("token", "", "The bridge's access token / api key / login")
|
var flagToken = flag.String("token", "", "The bridge's access token / api key / login")
|
||||||
var flagPair = flag.Bool("pair", false, "Try to pair with the bridge.")
|
var flagPair = flag.Bool("pair", false, "Try to pair with the bridge.")
|
||||||
@@ -25,8 +25,11 @@ var flagSearchTimeout = flag.Duration("search-timeout", time.Second*3, "Timeout
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// TODO: Select driver
|
// Find drivers
|
||||||
driver := nanoleaf.Driver{}
|
driver, err := config.DriverProvider().Provide(models.DriverKind(*flagDriver))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to find driver:", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Find bridge
|
// Find bridge
|
||||||
bridges, err := driver.SearchBridge(context.Background(), *flagAddress, !*flagPair)
|
bridges, err := driver.SearchBridge(context.Background(), *flagAddress, !*flagPair)
|
||||||
@@ -62,7 +65,7 @@ func main() {
|
|||||||
|
|
||||||
_ = driver.Publish(context.Background(), bridge, devices)
|
_ = driver.Publish(context.Background(), bridge, devices)
|
||||||
|
|
||||||
ch := make(chan models.Event)
|
ch := config.EventChannel
|
||||||
go func() {
|
go func() {
|
||||||
err := driver.Run(context.Background(), bridge, ch)
|
err := driver.Run(context.Background(), bridge, ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import (
|
|||||||
"git.aiterp.net/lucifer/new-server/app/config"
|
"git.aiterp.net/lucifer/new-server/app/config"
|
||||||
"github.com/pressly/goose"
|
"github.com/pressly/goose"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
db := config.DBX().DB
|
db := config.DBX().DB
|
||||||
|
|
||||||
|
log.Printf("Target version: %s",time.Now().Format("20060102150405"))
|
||||||
log.Printf("Database: %s:%d/%s", config.MySqlHost(), config.MySqlPort(), config.MySqlSchema())
|
log.Printf("Database: %s:%d/%s", config.MySqlHost(), config.MySqlPort(), config.MySqlSchema())
|
||||||
log.Printf("Authenticating as: %s", config.MySqlUsername())
|
log.Printf("Authenticating as: %s", config.MySqlUsername())
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package drivers
|
||||||
|
|
||||||
|
import "git.aiterp.net/lucifer/new-server/models"
|
||||||
|
|
||||||
|
type DriverMap map[models.DriverKind]models.Driver
|
||||||
|
|
||||||
|
func (m DriverMap) Provide(kind models.DriverKind) (models.Driver, error) {
|
||||||
|
if m[kind] == nil {
|
||||||
|
return nil, models.ErrUnknownDriver
|
||||||
|
}
|
||||||
|
|
||||||
|
return m[kind], nil
|
||||||
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ func (b *BridgeRepo) Find(ctx context.Context, id int) (models.Bridge, error) {
|
|||||||
|
|
||||||
func (b *BridgeRepo) FetchAll(ctx context.Context) ([]models.Bridge, error) {
|
func (b *BridgeRepo) FetchAll(ctx context.Context) ([]models.Bridge, error) {
|
||||||
bridges := make([]models.Bridge, 0, 8)
|
bridges := make([]models.Bridge, 0, 8)
|
||||||
err := b.DBX.GetContext(ctx, bridges, "SELECT * FROM bridge")
|
err := b.DBX.GetContext(ctx, &bridges, "SELECT * FROM bridge")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, dbErr(err)
|
return nil, dbErr(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.aiterp.net/lucifer/new-server/models"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type presetRecord struct {
|
||||||
|
ID int `db:"id"`
|
||||||
|
Name string `db:"name"`
|
||||||
|
Hue float64 `db:"hue"`
|
||||||
|
Saturation float64 `db:"saturation"`
|
||||||
|
Kelvin int `db:"kelvin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColorPresetRepo struct {
|
||||||
|
DBX *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorPresetRepo) Find(ctx context.Context, id int) (models.ColorPreset, error) {
|
||||||
|
var record presetRecord
|
||||||
|
err := c.DBX.GetContext(ctx, &record, "SELECT * FROM color_preset WHERE id = ?", id)
|
||||||
|
if err != nil {
|
||||||
|
return models.ColorPreset{}, dbErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.fromRecords(record)[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorPresetRepo) FetchAll(ctx context.Context) ([]models.ColorPreset, error) {
|
||||||
|
records := make([]presetRecord, 0, 16)
|
||||||
|
err := c.DBX.SelectContext(ctx, &records, "SELECT * FROM color_preset")
|
||||||
|
if err != nil {
|
||||||
|
return nil, dbErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.fromRecords(records...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorPresetRepo) Save(ctx context.Context, preset *models.ColorPreset) error {
|
||||||
|
if preset.ID > 0 {
|
||||||
|
_, err := c.DBX.ExecContext(
|
||||||
|
ctx,
|
||||||
|
"UPDATE color_preset SET name = ?, hue = ?, saturation = ?, kelvin = ? WHERE id = ?",
|
||||||
|
preset.Name, preset.Value.Hue, preset.Value.Saturation, preset.Value.Kelvin, preset.ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return dbErr(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rs, err := c.DBX.ExecContext(
|
||||||
|
ctx,
|
||||||
|
"INSERT INTO color_preset (name, hue, saturation, kelvin) VALUES (?, ?, ?, ?)",
|
||||||
|
preset.Name, preset.Value.Hue, preset.Value.Saturation, preset.Value.Kelvin,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return dbErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := rs.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return dbErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
preset.ID = int(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorPresetRepo) Delete(ctx context.Context, preset *models.ColorPreset) error {
|
||||||
|
_, err := c.DBX.ExecContext(ctx, "DELETE FROM color_preset WHERE id = ?", preset.ID)
|
||||||
|
if err != nil {
|
||||||
|
return dbErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
preset.ID = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorPresetRepo) fromRecords(records ...presetRecord) []models.ColorPreset {
|
||||||
|
newList := make([]models.ColorPreset, len(records), len(records))
|
||||||
|
for i, record := range records {
|
||||||
|
newList[i] = models.ColorPreset{
|
||||||
|
ID: record.ID,
|
||||||
|
Name: record.Name,
|
||||||
|
Value: models.ColorValue{
|
||||||
|
Hue: record.Hue,
|
||||||
|
Saturation: record.Saturation,
|
||||||
|
Kelvin: record.Kelvin,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newList
|
||||||
|
}
|
||||||
+1
-1
@@ -21,7 +21,7 @@ type DriverKind string
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
DTHue DriverKind = "Hue"
|
DTHue DriverKind = "Hue"
|
||||||
DTNanoLeaf DriverKind = "NanoLeaf"
|
DTNanoLeaf DriverKind = "Nanoleaf"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ValidDriverKinds = []DriverKind{
|
var ValidDriverKinds = []DriverKind{
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ColorPreset struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value ColorValue `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColorPresetRepository interface {
|
||||||
|
Find(ctx context.Context, id int) (ColorPreset, error)
|
||||||
|
FetchAll(ctx context.Context) ([]ColorPreset, error)
|
||||||
|
Save(ctx context.Context, preset *ColorPreset) error
|
||||||
|
Delete(ctx context.Context, preset *ColorPreset) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorPreset) Validate() {
|
||||||
|
c.Name = strings.Trim(c.Name, " \t\n")
|
||||||
|
if len(c.Name) == 0 {
|
||||||
|
c.Name = c.Value.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ var ErrBadInput = errors.New("bad input")
|
|||||||
var ErrBadColor = errors.New("bad color")
|
var ErrBadColor = errors.New("bad color")
|
||||||
var ErrInternal = errors.New("internal")
|
var ErrInternal = errors.New("internal")
|
||||||
var ErrUnknownColorFormat = errors.New("unknown color format")
|
var ErrUnknownColorFormat = errors.New("unknown color format")
|
||||||
|
var ErrUnknownDriver = errors.New("unknown driver")
|
||||||
|
|
||||||
var ErrMissingToken = errors.New("driver is missing authentication information")
|
var ErrMissingToken = errors.New("driver is missing authentication information")
|
||||||
var ErrIncorrectToken = errors.New("driver is not accepting authentication information")
|
var ErrIncorrectToken = errors.New("driver is not accepting authentication information")
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE color_preset
|
||||||
|
(
|
||||||
|
id INT NOT NULL AUTO_INCREMENT,
|
||||||
|
hue DOUBLE NOT NULL,
|
||||||
|
saturation DOUBLE NOT NULL,
|
||||||
|
kelvin INT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE color_preset;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE color_preset
|
||||||
|
ADD COLUMN name VARCHAR(255) NOT NULL DEFAULT 'Unnamed';
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE color_preset
|
||||||
|
DROP COLUMN name;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@jaames/iro": "^5.5.1",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
|
"react-semantic-ui-range": "^0.7.1",
|
||||||
"typescript": "^4.1.2",
|
"typescript": "^4.1.2",
|
||||||
"web-vitals": "^1.0.1"
|
"web-vitals": "^1.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
+6
-1
@@ -1,9 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {HookRouter, navigate, usePath, useRoutes} from "hookrouter";
|
import {HookRouter, navigate, usePath, useRoutes} from "hookrouter";
|
||||||
import {Tabs} from "./primitives/Layout";
|
import {Tabs} from "./primitives/Layout";
|
||||||
|
import {HSColorPicker} from "./primitives/Forms";
|
||||||
|
|
||||||
const routeObj: HookRouter.RouteObject = {
|
const routeObj: HookRouter.RouteObject = {
|
||||||
"/": () => <div>1</div>,
|
"/": () => (
|
||||||
|
<div>
|
||||||
|
<HSColorPicker h={60} s={1} onChange={() => void(0)}/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
"/devices": () => <div>2</div>,
|
"/devices": () => <div>2</div>,
|
||||||
"/settings": () => <div>3</div>,
|
"/settings": () => <div>3</div>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import React, {useLayoutEffect, useState} from 'react';
|
||||||
|
// @ts-ignore
|
||||||
|
import iro from "@jaames/iro";
|
||||||
|
|
||||||
|
interface ColorPickerProps {
|
||||||
|
h: number
|
||||||
|
s: number
|
||||||
|
onChange: (h: number, v: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomId = () => Math.floor(Math.random() * 100000);
|
||||||
|
|
||||||
|
export const HSColorPicker: React.FC<ColorPickerProps> = ({h, s, onChange}) => {
|
||||||
|
const [random] = useState(() => `color-picker-${randomId()}`);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
const colorPicker = new iro.ColorPicker(`#${random}`, {
|
||||||
|
color: {h, s: s * 100, v: 255},
|
||||||
|
layout: [
|
||||||
|
{
|
||||||
|
component: iro.ui.Wheel,
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
colorPicker.on("input:end", (color: { hsv: { h: number, s: number } }) => {
|
||||||
|
onChange(color.hsv.h || 0, (color.hsv.s || 0) / 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const elem = document.getElementById(`color-picker-${random}`);
|
||||||
|
if (elem === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.innerHTML = "";
|
||||||
|
};
|
||||||
|
}, [h, s, onChange, random]);
|
||||||
|
|
||||||
|
return <div id={random} style={{margin: "0 auto"}}/>;
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {useCallback, useEffect, useMemo} from "react";
|
import React, {useCallback, useEffect} from "react";
|
||||||
import "./Layout.sass";
|
import "./Layout.sass";
|
||||||
|
|
||||||
interface TabsProps {
|
interface TabsProps {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
$color-background: #111
|
$color-background: #111
|
||||||
$color-foreground: rgb(238, 238, 238)
|
$color-foreground: rgb(204, 204, 204)
|
||||||
$color-foreground-dark: rgba(238, 238, 238, 0.05)
|
$color-foreground-dark: rgba(238, 238, 238, 0.05)
|
||||||
|
|
||||||
body, html
|
body, html
|
||||||
|
|||||||
@@ -1209,6 +1209,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^8.3.0"
|
"@hapi/hoek" "^8.3.0"
|
||||||
|
|
||||||
|
"@irojs/iro-core@^1.2.0":
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@irojs/iro-core/-/iro-core-1.2.0.tgz#3587c2db7a6de09f76dbf75b94605ac251039ca8"
|
||||||
|
integrity sha512-RVxd4lEx3KPXRBYo7urLSr9C3PJsm3t9AgPQppP5LDqQcMrXCXcZoeneMtQndMk60k72NziNiPpbZlZIvcT4VQ==
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||||
@@ -1225,6 +1230,14 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
|
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
|
||||||
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
|
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
|
||||||
|
|
||||||
|
"@jaames/iro@^5.5.1":
|
||||||
|
version "5.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jaames/iro/-/iro-5.5.1.tgz#1935713fe3cca8c4eadcfc6013b6f846ac8be097"
|
||||||
|
integrity sha512-HR2phfXjEINFKXxEdbGqp9/MfHfQ/xP+nLi2gjNV4RHB3tBOgHMpS1WFMU7fp1vl//vWXxKxVKa91D+5JoFgMA==
|
||||||
|
dependencies:
|
||||||
|
"@irojs/iro-core" "^1.2.0"
|
||||||
|
preact "^10.0.0"
|
||||||
|
|
||||||
"@jest/console@^26.6.2":
|
"@jest/console@^26.6.2":
|
||||||
version "26.6.2"
|
version "26.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2"
|
resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2"
|
||||||
@@ -9070,6 +9083,11 @@ postcss@^8.1.0:
|
|||||||
nanoid "^3.1.20"
|
nanoid "^3.1.20"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
preact@^10.0.0:
|
||||||
|
version "10.5.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.13.tgz#85f6c9197ecd736ce8e3bec044d08fd1330fa019"
|
||||||
|
integrity sha512-q/vlKIGNwzTLu+jCcvywgGrt+H/1P/oIRSD6mV4ln3hmlC+Aa34C7yfPI4+5bzW8pONyVXYS7SvXosy2dKKtWQ==
|
||||||
|
|
||||||
prelude-ls@^1.2.1:
|
prelude-ls@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
@@ -9449,6 +9467,11 @@ react-scripts@4.0.3:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "^2.1.3"
|
fsevents "^2.1.3"
|
||||||
|
|
||||||
|
react-semantic-ui-range@^0.7.1:
|
||||||
|
version "0.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-semantic-ui-range/-/react-semantic-ui-range-0.7.1.tgz#bc552f889e4243d500d4673dd7966752664935fe"
|
||||||
|
integrity sha512-6AQLuFeARHcIO7yl4Pd0BBIB9sJyeNK1mku7luK6aTXrj9EGaqr1fWS7w4b0+GUP7CvkixmcyYqykxzpKS4Lcw==
|
||||||
|
|
||||||
react@^17.0.2:
|
react@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|||||||
Reference in New Issue
Block a user