refactor: JsonTabs

This commit is contained in:
cha0s 2021-02-03 01:17:10 -06:00
parent 60a308a0ed
commit ac883688d0
6 changed files with 250 additions and 171 deletions

View File

@ -27,7 +27,8 @@
"autoprefixer": "^9.8.6",
"debug": "4.3.1",
"fast-json-patch": "^3.0.0-1",
"react-json-editor-ajrm": "^2.5.13"
"react-json-editor-ajrm": "^2.5.13",
"react-tabs": "^3.1.2"
},
"devDependencies": {
"@latus/redux": "^2.0.0",

View File

@ -0,0 +1,155 @@
import {join} from 'path';
import {
PropTypes,
React,
useEffect,
usePrevious,
useRef,
useState,
} from '@latus/react';
import {
Tab,
Tabs,
TabList,
TabPanel,
} from 'react-tabs';
import useJsonPatcher from '../../hooks/use-json-patcher';
const TabMap = ({
createPanel,
defaultValue,
path,
map,
}) => {
const patch = useJsonPatcher();
const editingRef = useRef();
useEffect(() => {
editingRef.current?.focus();
});
const [isEditing, setIsEditing] = useState(false);
const previousEditing = usePrevious(isEditing);
const [previousKey, setPreviousKey] = useState(false);
useEffect(() => {
setTimeout(() => {
if (false === previousEditing) {
editingRef.current?.select();
}
}, 10);
}, [editingRef, isEditing, previousEditing]);
const tabs = [];
const tabPanels = [];
Object.entries(map).forEach(([key, value]) => {
const confirmEdit = () => {
if (isEditing && previousKey !== isEditing) {
patch({
op: 'move',
from: join(path, previousKey),
path: join(path, isEditing),
});
}
setIsEditing(false);
setPreviousKey(false);
};
tabs.push(
<Tab
key={key}
onDoubleClick={() => {
setPreviousKey(key);
setIsEditing(key);
}}
>
{
key === previousKey
? (
<input
type="text"
value={isEditing}
onBlur={confirmEdit}
onChange={({target: {value}}) => {
setPreviousKey(key);
setIsEditing(value);
}}
onKeyPress={({key}) => {
if ('Enter' === key) {
confirmEdit();
}
}}
ref={editingRef}
/>
)
: key
}
&nbsp;
<button
type="button"
className="json-tabs__remove"
onClick={() => {
patch({
op: 'remove',
path: join(path, key),
});
}}
>
</button>
</Tab>,
);
tabPanels.push(
<TabPanel key={key}>
{createPanel(value, key)}
</TabPanel>,
);
});
return (
<div className="json-tabs">
<Tabs>
<div className="json-tabs__bar">
<button
className="json-tabs__add"
onClick={() => {
let i = 1;
let key = 'untitled';
while (map[key]) {
key = `untitled (${i++})`;
}
patch({
op: 'add',
path: join(path, key),
value: defaultValue,
});
setIsEditing(key);
setPreviousKey(key);
}}
type="button"
>
+
</button>
<TabList>
{tabs}
</TabList>
</div>
<div className="json-tabs__panes">
{tabPanels}
</div>
</Tabs>
</div>
);
};
TabMap.defaultProps = {
path: '/',
};
TabMap.displayName = 'TabMap';
TabMap.propTypes = {
createPanel: PropTypes.func.isRequired,
// eslint-disable-next-line react/forbid-prop-types
defaultValue: PropTypes.any.isRequired,
map: PropTypes.ObjectOf(PropTypes.any).isRequired,
path: PropTypes.string,
};
export default TabMap;

View File

@ -0,0 +1,14 @@
.json-tabs__bar {
display: flex;
margin-bottom: 1em;
overflow: auto;
}
.json-tabs__add {
background-color: #171717;
border: none;
margin: 0;
margin-right: 1em;
min-width: 2.5em;
height: 2.5em;
}

View File

@ -2,6 +2,7 @@ import JsonResourceController from './resource-controllers/json';
import {patchJsonResource} from './state/json';
import reducer from './state/reducer';
export {default as JsonTabs} from './components/json-tabs';
export {default as JsonComponent} from './components/json';
export {default as useJsonPatcher} from './hooks/use-json-patcher';
export * from './state';

View File

@ -2579,6 +2579,11 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
clsx@^1.1.0:
version "1.1.1"
resolved "http://npm.cha0sdev/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
code-point-at@^1.0.0:
version "1.1.0"
resolved "http://npm.cha0sdev/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@ -6741,7 +6746,7 @@ promise-inflight@^1.0.1:
resolved "http://npm.cha0sdev/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
prop-types@^15.6.1, prop-types@^15.7.2:
prop-types@^15.5.0, prop-types@^15.6.1, prop-types@^15.7.2:
version "15.7.2"
resolved "http://npm.cha0sdev/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -6952,6 +6957,14 @@ react-redux@^7.2.2:
prop-types "^15.7.2"
react-is "^16.13.1"
react-tabs@^3.1.2:
version "3.1.2"
resolved "http://npm.cha0sdev/react-tabs/-/react-tabs-3.1.2.tgz#9047ad7d8a53d357a67c14ad4c4a64cc16660fa8"
integrity sha512-OKS1l7QzSNcn+L2uFsxyGFHdXp9YsPGf/YOURWcImp7xLN36n0Wz+/j9HwlwGtlXCZexwshScR5BrcFbw/3P9Q==
dependencies:
clsx "^1.1.0"
prop-types "^15.5.0"
react-virtualized-auto-sizer@^1.0.2:
version "1.0.4"
resolved "http://npm.cha0sdev/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.4.tgz#2b83adb5ecdee8dcb33784e96ee0074eb5369665"

View File

@ -14,8 +14,6 @@ import {
React,
useEffect,
useLatus,
usePrevious,
useRef,
useState,
} from '@latus/react';
import {
@ -23,35 +21,18 @@ import {
Stage,
Vector as VectorComponent,
} from '@persea/core';
import {useJsonPatcher} from '@persea/json';
import {
Tab,
Tabs,
TabList,
TabPanel,
} from 'react-tabs';
JsonTabs,
useJsonPatcher,
} from '@persea/json';
import AnimationVisualization from '../components/animation-visualization';
const Animated = ({json, path}) => {
const patch = useJsonPatcher();
const latus = useLatus();
const editingRef = useRef();
useEffect(() => {
editingRef.current?.focus();
});
const [isEditing, setIsEditing] = useState(false);
const previousEditing = usePrevious(isEditing);
const [animations, setAnimations] = useState({});
const [animationViews, setAnimationViews] = useState({});
const [previousKey, setPreviousKey] = useState(false);
useEffect(() => {
setTimeout(() => {
if (false === previousEditing) {
editingRef.current?.select();
}
}, 10);
}, [editingRef, isEditing, previousEditing]);
useEffect(() => {
const {Animation} = latus.get('%resources');
setAnimations({});
@ -109,68 +90,13 @@ const Animated = ({json, path}) => {
};
loadJson();
}, [latus, json]);
const tabs = [];
const tabPanels = [];
Object.entries(json.params.animations).forEach(([key, animation]) => {
const routinePath = join(path, 'params/animations', key);
const confirmEdit = () => {
if (isEditing && previousKey !== isEditing) {
patch({
op: 'move',
from: join(path, 'params/animations', previousKey),
path: join(path, 'params/animations', isEditing),
});
}
setIsEditing(false);
setPreviousKey(false);
};
tabs.push(
<Tab
key={key}
onDoubleClick={() => {
setPreviousKey(key);
setIsEditing(key);
}}
>
{
key === previousKey
? (
<input
type="text"
value={isEditing}
onBlur={confirmEdit}
onChange={({target: {value}}) => {
setPreviousKey(key);
setIsEditing(value);
}}
onKeyPress={({key}) => {
if ('Enter' === key) {
confirmEdit();
}
}}
ref={editingRef}
/>
)
: key
}
&nbsp;
<button
type="button"
className="routines__tab-remove"
onClick={() => {
patch({
op: 'remove',
path: routinePath,
});
}}
>
</button>
</Tab>,
);
tabPanels.push(
<TabPanel key={key}>
return (
<div className="animated">
<div className="label">
<div className="vertical">Animations</div>
<JsonTabs
createPanel={(animation, key) => (
<>
{animations[key] && (
<div className="animated__visualization">
<AnimationVisualization json={animations[key].toJSON()} />
@ -219,47 +145,16 @@ const Animated = ({json, path}) => {
value={animation.jitter}
/>
</label>
</TabPanel>,
);
});
return (
<div className="animated">
<div className="label">
<div className="vertical">Animations</div>
<Tabs>
<div className="animated__animations-tab-bar">
<button
className="animated__add-animation"
onClick={() => {
let i = 1;
let name = 'untitled';
while (json.params.animations[name]) {
name = `untitled (${i++})`;
}
patch({
op: 'add',
path: join(path, 'params/animations', name),
value: {
</>
)}
defaultValue={{
extends: '',
offset: [0, 0],
jitter: 0,
},
});
setIsEditing(name);
setPreviousKey(name);
}}
type="button"
>
+
</button>
<TabList>
{tabs}
</TabList>
</div>
<div className="routines__tab-panes">
{tabPanels}
</div>
</Tabs>
map={json.params.animations}
path={join(path, 'params/animations')}
/>
</div>
</div>
);