refactor: JsonTabs
This commit is contained in:
parent
60a308a0ed
commit
ac883688d0
|
@ -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",
|
||||
|
|
155
packages/json/src/components/json-tabs/index.jsx
Normal file
155
packages/json/src/components/json-tabs/index.jsx
Normal 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
|
||||
}
|
||||
|
||||
<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;
|
14
packages/json/src/components/json-tabs/index.scss
Normal file
14
packages/json/src/components/json-tabs/index.scss
Normal 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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,157 +90,71 @@ 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
|
||||
}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="routines__tab-remove"
|
||||
onClick={() => {
|
||||
patch({
|
||||
op: 'remove',
|
||||
path: routinePath,
|
||||
});
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
</Tab>,
|
||||
);
|
||||
tabPanels.push(
|
||||
<TabPanel key={key}>
|
||||
{animations[key] && (
|
||||
<div className="animated__visualization">
|
||||
<AnimationVisualization json={animations[key].toJSON()} />
|
||||
<Stage
|
||||
renderable={animationViews[key]}
|
||||
scalable={false}
|
||||
size={animations[key].frameSize}
|
||||
ticker={() => {}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<label>
|
||||
Extends
|
||||
<input
|
||||
onChange={(event) => {
|
||||
patch({
|
||||
path: join(path, 'params/animations', key, 'extends'),
|
||||
value: event.target.value,
|
||||
});
|
||||
}}
|
||||
type="text"
|
||||
value={animation.extends}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Offset
|
||||
<VectorComponent
|
||||
onChange={(event, value) => {
|
||||
patch({
|
||||
path: join(path, 'params/animations', key, 'offset'),
|
||||
value,
|
||||
});
|
||||
}}
|
||||
value={animation.offset}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Jitter
|
||||
<Number
|
||||
onChange={(event, value) => {
|
||||
patch({
|
||||
path: join(path, 'params/animations', key, 'jitter'),
|
||||
value,
|
||||
});
|
||||
}}
|
||||
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: {
|
||||
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>
|
||||
<JsonTabs
|
||||
createPanel={(animation, key) => (
|
||||
<>
|
||||
{animations[key] && (
|
||||
<div className="animated__visualization">
|
||||
<AnimationVisualization json={animations[key].toJSON()} />
|
||||
<Stage
|
||||
renderable={animationViews[key]}
|
||||
scalable={false}
|
||||
size={animations[key].frameSize}
|
||||
ticker={() => {}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<label>
|
||||
Extends
|
||||
<input
|
||||
onChange={(event) => {
|
||||
patch({
|
||||
path: join(path, 'params/animations', key, 'extends'),
|
||||
value: event.target.value,
|
||||
});
|
||||
}}
|
||||
type="text"
|
||||
value={animation.extends}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Offset
|
||||
<VectorComponent
|
||||
onChange={(event, value) => {
|
||||
patch({
|
||||
path: join(path, 'params/animations', key, 'offset'),
|
||||
value,
|
||||
});
|
||||
}}
|
||||
value={animation.offset}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Jitter
|
||||
<Number
|
||||
onChange={(event, value) => {
|
||||
patch({
|
||||
path: join(path, 'params/animations', key, 'jitter'),
|
||||
value,
|
||||
});
|
||||
}}
|
||||
value={animation.jitter}
|
||||
/>
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
defaultValue={{
|
||||
extends: '',
|
||||
offset: [0, 0],
|
||||
jitter: 0,
|
||||
}}
|
||||
map={json.params.animations}
|
||||
path={join(path, 'params/animations')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue
Block a user