feat: first step toward mutability
This commit is contained in:
parent
2de1665426
commit
d2e26d3727
|
@ -29,7 +29,7 @@ const EntityComponent = (props) => {
|
||||||
<div className="entity">
|
<div className="entity">
|
||||||
<div className="document-pane" />
|
<div className="document-pane" />
|
||||||
<div className="settings-pane">
|
<div className="settings-pane">
|
||||||
<Traits context={entity.context} traits={traits} />
|
<Traits context={entity.context} target={json.uuid} traits={traits} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -46,7 +46,15 @@ export const selectEntityByUri = createSelector(
|
||||||
const slice = createSlice({
|
const slice = createSlice({
|
||||||
name: 'entities',
|
name: 'entities',
|
||||||
initialState: adapter.getInitialState({uris: {}}),
|
initialState: adapter.getInitialState({uris: {}}),
|
||||||
reducers: {},
|
reducers: {
|
||||||
|
setTraitProperty: (state, {payload: {target, trait, type, key, value}}) => {
|
||||||
|
const {traits} = state.entities[target];
|
||||||
|
traits[trait] = traits[trait] || {};
|
||||||
|
traits[trait][type] = traits[trait][type] || {};
|
||||||
|
traits[trait][type][key] = value;
|
||||||
|
state.entities[target].traits = traits;
|
||||||
|
},
|
||||||
|
},
|
||||||
extraReducers: {
|
extraReducers: {
|
||||||
[fetchEntity.fulfilled]: (state, action) => {
|
[fetchEntity.fulfilled]: (state, action) => {
|
||||||
adapter.upsertMany(state, action.payload.entities);
|
adapter.upsertMany(state, action.payload.entities);
|
||||||
|
@ -56,4 +64,8 @@ const slice = createSlice({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
setTraitProperty,
|
||||||
|
} = slice.actions;
|
||||||
|
|
||||||
export default slice.reducer;
|
export default slice.reducer;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {produce} from 'immer';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
// import ReactMarkdown from 'react-markdown';
|
// import ReactMarkdown from 'react-markdown';
|
||||||
|
import {useDispatch} from 'react-redux';
|
||||||
import {
|
import {
|
||||||
Tab,
|
Tab,
|
||||||
Tabs,
|
Tabs,
|
||||||
|
@ -17,6 +18,8 @@ import {deregisterHooks, registerHooks} from 'scwp';
|
||||||
|
|
||||||
import Value from '~/client/value';
|
import Value from '~/client/value';
|
||||||
|
|
||||||
|
import {setTraitProperty} from './state';
|
||||||
|
|
||||||
const SCROLL_MAG = 80;
|
const SCROLL_MAG = 80;
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
|
@ -44,7 +47,7 @@ const ensureTraitComponents = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeTabSelector = (context, type) => createSelector(
|
const makeTabSelector = (context, dispatch, target, type) => createSelector(
|
||||||
(_) => _,
|
(_) => _,
|
||||||
(trait) => {
|
(trait) => {
|
||||||
ensureTraitComponents();
|
ensureTraitComponents();
|
||||||
|
@ -55,7 +58,7 @@ const makeTabSelector = (context, type) => createSelector(
|
||||||
const state = Trait ? {...Trait.defaultState(), ...stateRaw} : stateRaw;
|
const state = Trait ? {...Trait.defaultState(), ...stateRaw} : stateRaw;
|
||||||
const paramsDescription = Trait?.describeParams() || {};
|
const paramsDescription = Trait?.describeParams() || {};
|
||||||
const stateDescription = Trait?.describeState() || {};
|
const stateDescription = Trait?.describeState() || {};
|
||||||
const renderComponents = (description, values) => (
|
const renderComponents = (traitHalf, description, values) => (
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
Object.values(mapObject(description, (description, key) => {
|
Object.values(mapObject(description, (description, key) => {
|
||||||
const {label, options} = description;
|
const {label, options} = description;
|
||||||
|
@ -68,6 +71,13 @@ const makeTabSelector = (context, type) => createSelector(
|
||||||
<div className="invisible-separator" />
|
<div className="invisible-separator" />
|
||||||
<Value.Component
|
<Value.Component
|
||||||
context={context}
|
context={context}
|
||||||
|
onChange={(value) => dispatch(setTraitProperty({
|
||||||
|
target,
|
||||||
|
trait: type,
|
||||||
|
type: traitHalf,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
}))}
|
||||||
options={options}
|
options={options}
|
||||||
value={values[key]}
|
value={values[key]}
|
||||||
/>
|
/>
|
||||||
|
@ -79,55 +89,56 @@ const makeTabSelector = (context, type) => createSelector(
|
||||||
? <TraitComponent params={params} state={state} />
|
? <TraitComponent params={params} state={state} />
|
||||||
: (
|
: (
|
||||||
<form className="trait-component-builtin">
|
<form className="trait-component-builtin">
|
||||||
{renderComponents(paramsDescription, params)}
|
{renderComponents('params', paramsDescription, params)}
|
||||||
{renderComponents(stateDescription, state)}
|
{renderComponents('state', stateDescription, state)}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const Traits = (props) => {
|
const Traits = (props) => {
|
||||||
const {context, traits} = props;
|
const {context, target, traits} = props;
|
||||||
const [tabSelectorMap, setTabSelectorMap] = useState({});
|
const dispatch = useDispatch();
|
||||||
|
const [traitForms, setTraitForms] = useState({});
|
||||||
|
const [mts, setMts] = useState();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!mts) {
|
||||||
|
setMts(() => (type) => makeTabSelector(context, dispatch, target, type));
|
||||||
|
}
|
||||||
|
}, [mts, setMts]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const entries = Object.entries(traits);
|
const entries = Object.entries(traits);
|
||||||
setTabSelectorMap(produce(tabSelectorMap, (draft) => {
|
setTraitForms(produce(traitForms, (draft) => {
|
||||||
for (let i = 0; i < entries.length; i++) {
|
for (let i = 0; i < entries.length; i++) {
|
||||||
const [type] = entries[i];
|
const [type] = entries[i];
|
||||||
if (!draft[type]) {
|
draft[type] = mts(type);
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
draft[type] = new WeakMap();
|
|
||||||
}
|
|
||||||
if (!draft[type].has(traits)) {
|
|
||||||
draft[type].set(traits, makeTabSelector(context, type));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const M = {
|
const M = {
|
||||||
autoreg$accept: (type, M2) => {
|
autoreg$accept: (type, M2) => {
|
||||||
if ('trait' === type) {
|
if ('trait' === type) {
|
||||||
const {default: Trait} = M2;
|
const {default: Trait} = M2;
|
||||||
setTabSelectorMap(produce(tabSelectorMap, (draft) => {
|
setTraitForms(produce(traitForms, (draft) => {
|
||||||
const traitType = Trait.type();
|
draft[Trait.type()] = mts(Trait.type());
|
||||||
if (!draft[traitType]) {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
draft[traitType] = new WeakMap();
|
|
||||||
}
|
|
||||||
draft[traitType].set(traits, makeTabSelector(traitType));
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
registerHooks(M, __filename);
|
registerHooks(M, __filename);
|
||||||
return () => deregisterHooks(M);
|
return () => deregisterHooks(M);
|
||||||
}, [context, tabSelectorMap, traits]);
|
}, [mts, traits]);
|
||||||
const [tabIndex, setTabIndex] = useState(0);
|
const [tabIndex, setTabIndex] = useState(0);
|
||||||
let listRef;
|
let listRef;
|
||||||
const entries = Object.entries(traits);
|
const entries = Object.entries(traits);
|
||||||
const tabs = [];
|
const tabs = [];
|
||||||
for (let i = 0; i < entries.length; i++) {
|
for (let i = 0; i < entries.length; i++) {
|
||||||
const [type, trait] = entries[i];
|
const [type, trait] = entries[i];
|
||||||
tabs.push([type, tabSelectorMap[type]?.get(traits)(trait)]);
|
if (type in traitForms) {
|
||||||
|
tabs.push([type, traitForms[type](trait)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="traits">
|
<div className="traits">
|
||||||
|
|
|
@ -14,6 +14,7 @@ const decorate = compose(
|
||||||
|
|
||||||
const Literal = ({
|
const Literal = ({
|
||||||
context,
|
context,
|
||||||
|
onChange,
|
||||||
value,
|
value,
|
||||||
}) => {
|
}) => {
|
||||||
const typeRenderers = useTypeRenderers();
|
const typeRenderers = useTypeRenderers();
|
||||||
|
@ -36,7 +37,7 @@ const Literal = ({
|
||||||
.map((tierOption) => <option key={tierOption}>{tierOption}</option>)
|
.map((tierOption) => <option key={tierOption}>{tierOption}</option>)
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
{Component ? <Component value={value.value} /> : null}
|
{Component ? <Component onChange={onChange} value={value.value} /> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
||||||
import propTypes from './prop-types';
|
import propTypes from './prop-types';
|
||||||
|
|
||||||
const String = ({
|
const String = ({
|
||||||
|
onChange,
|
||||||
options,
|
options,
|
||||||
value,
|
value,
|
||||||
}) => (
|
}) => (
|
||||||
|
@ -17,7 +18,13 @@ const String = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
)
|
)
|
||||||
: <input readOnly type="text" value={value} />
|
: (
|
||||||
|
<input
|
||||||
|
onChange={(event) => onChange(event.target.value)}
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,6 +14,7 @@ const decorate = compose(
|
||||||
|
|
||||||
const Value = ({
|
const Value = ({
|
||||||
context,
|
context,
|
||||||
|
onChange,
|
||||||
options,
|
options,
|
||||||
value,
|
value,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -25,7 +26,7 @@ const Value = ({
|
||||||
<span className="value">
|
<span className="value">
|
||||||
{
|
{
|
||||||
Component
|
Component
|
||||||
? <Component context={context} options={options} value={value} />
|
? <Component context={context} onChange={onChange} options={options} value={value} />
|
||||||
: <div className="unrenderable" title={`No renderer for '${type}' found!`}>?</div>
|
: <div className="unrenderable" title={`No renderer for '${type}' found!`}>?</div>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user