From ec166833928b469143c8ee1cc88d0260987ad921 Mon Sep 17 00:00:00 2001 From: emmelleppi Date: Wed, 4 Nov 2020 19:01:41 +0100 Subject: [PATCH] Adds set and get initial tweaks data from the url hash --- example/src/App.tsx | 10 ++++----- package.json | 1 + src/types.ts | 5 ++++- src/useTweaks.ts | 20 ++++++++++++----- src/utils.ts | 53 +++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 19 ++++++++++++++++ 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 9ab928a..75eaaae 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -2,7 +2,7 @@ import * as THREE from 'three' import React, { Suspense, useRef } from 'react' import { Canvas, useFrame } from 'react-three-fiber' import { OrbitControls, ContactShadows, useGLTF, useCubeTexture, Octahedron } from '@react-three/drei' -import { useTweaks, makeFolder, makeSeparator, makeButton } from 'use-tweaks' +import { useTweaks, makeFolder, makeSeparator, makeButton } from '../../src' import Badge from './Badge' @@ -20,7 +20,7 @@ function Suzanne(props) { scale: { value: 1, max: 3 }, ...makeButton('Log Console', () => console.log('something in the console ' + Date.now())), }), - }) + }, { setGetFromUrl: true }) return ( @@ -32,7 +32,7 @@ function Suzanne(props) { function Octa({ envMap }) { const mesh = useRef() - const { move } = useTweaks('Octa', { move: true }) + const { move } = useTweaks('Octa', { move: true }, { setGetFromUrl: true }, ) useFrame(({ clock }) => { if (move) { @@ -52,7 +52,7 @@ function Scene() { const { model } = useTweaks({ model: { value: 'Suzanne', options: ['suzanne', 'Octahedron'] }, - }) + }, { setGetFromUrl: true }) return ( @@ -63,7 +63,7 @@ function Scene() { export default function App() { const ref = React.useRef(null) - const { color } = useTweaks({ color: { value: '#f2f2f2', label: 'background' } }, { container: ref }) + const { color } = useTweaks({ color: { value: '#f2f2f2', label: 'background' } }, { container: ref, setGetFromUrl: true }) return ( <> diff --git a/package.json b/package.json index 68b1ca9..baf38fa 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ ], "dependencies": { "get-value": "^3.0.1", + "query-string": "^6.13.6", "set-value": "^3.0.2" } } diff --git a/src/types.ts b/src/types.ts index 70f7c9f..d29c74c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,7 +19,10 @@ export interface Schema { [name: string]: InputtableOutType | InputConstructor | Folder | Separator } -export type Settings = Omit & { container?: React.RefObject } +export type Settings = Omit & { + container?: React.RefObject + setGetFromUrl?: boolean +} export interface Folder { type: SpecialInputTypes diff --git a/src/useTweaks.ts b/src/useTweaks.ts index 74461c2..946ea84 100644 --- a/src/useTweaks.ts +++ b/src/useTweaks.ts @@ -1,8 +1,9 @@ -import { useState, useLayoutEffect, useRef } from 'react' +import { useState, useLayoutEffect, useRef, useEffect } from 'react' import Tweakpane from 'tweakpane' -import { getData, buildPane } from './data' +import { buildPane, getData } from './data' import { Schema, Settings, UseTweaksValues } from './types' +import { getInitialDataFromUrl, setUrlFromData } from './utils' let ROOTPANE: Tweakpane | undefined @@ -13,11 +14,18 @@ export function useTweaks( ): UseTweaksValues { const _name = typeof nameOrSchema === 'string' ? nameOrSchema : undefined const _rootKey = typeof nameOrSchema === 'string' ? 'root.' + nameOrSchema : 'root' - const _settings = useRef(typeof nameOrSchema === 'string' ? settings : (schemaOrSettings as Settings)) - const _schema = useRef(typeof nameOrSchema === 'string' ? (schemaOrSettings as T) : nameOrSchema) + + const rawSettings = typeof nameOrSchema === 'string' ? settings : (schemaOrSettings as Settings) + const { setGetFromUrl, ...remainingSettings } = rawSettings || {} + const _settings = useRef(remainingSettings) + const rawSchema = typeof nameOrSchema === 'string' ? (schemaOrSettings as T) : nameOrSchema + const _schema = useRef(setGetFromUrl ? getInitialDataFromUrl(rawSchema, _rootKey) : rawSchema) + const [data, set] = useState(() => getData(_schema.current, _rootKey)) - + + useEffect(() => void (setGetFromUrl && setUrlFromData(data, _rootKey)), [setGetFromUrl, data, _rootKey]) + useLayoutEffect(() => { ROOTPANE = ROOTPANE || new Tweakpane({ ..._settings, container: _settings.current?.container?.current! }) const isRoot = _name === undefined @@ -33,4 +41,4 @@ export function useTweaks( }, [_name, _rootKey]) return data as UseTweaksValues -} +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 8617082..af648b5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,56 @@ +import queryString from "query-string" + +import { Schema } from "./types"; + export function uuid(): string { return `${Math.floor((new Date().getTime() * Math.random()) / 1000)}` } + +export function isJson(str: string) : boolean { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; +} + +export function setUrlFromData(data : any, rootKey : string) { + const parsed = queryString.parse(document.location.hash); + const reducedData = Object.entries(data).reduce((acc: any, [key, val]) => { + if (typeof val !== "undefined") { + acc[`${rootKey}$${key}`] = typeof val === "object" ? JSON.stringify(val) : val + } + return acc + }, parsed) + const stringified = queryString.stringify(reducedData); + document.location.hash = `#${stringified}`; +} + +export function getInitialDataFromUrl(schema : Schema, rootKey : string) { + const parsed = queryString.parse(document.location.hash); + return Object.entries(parsed).reduce((acc, [key, val]:[string, any]) => { + const splittedKey = key.split("$") + if (splittedKey[0] === rootKey) { + setValueInSchema(acc, splittedKey[1], val) + } + return acc + }, schema) +} + +function setValueInSchema(schema : any, key : string, value : any) { + const correctValue = isJson(value) ? JSON.parse(value) : value + if (schema[key]) { + if (schema[key].value) { + schema[key].value = correctValue + } else { + schema[key] = correctValue + } + } else if (typeof schema === "object") { + Object.entries(schema).forEach(([_, subSchema] : [string, any]) => { + if (subSchema?.type === 1) { + setValueInSchema(subSchema.schema, key, value) + } + }) + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 1407c06..c1f9d89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7157,6 +7157,15 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^6.13.6: + version "6.13.6" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.6.tgz#e5ac7c74f2a5da43fbca0b883b4f0bafba439966" + integrity sha512-/WWZ7d9na6s2wMEGdVCVgKWE9Rt7nYyNIf7k8xmHXcesPMlEzicWo3lbYwHyA4wBktI2KrXxxZeACLbE84hvSQ== + dependencies: + decode-uri-component "^0.2.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -8025,6 +8034,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -8119,6 +8133,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + string-length@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837"