Improve concept derivation UI

This commit is contained in:
IRBorisov 2024-04-05 20:04:12 +03:00
parent f03fd337ba
commit 2fbbec0466
13 changed files with 60 additions and 23 deletions

View File

@ -11,18 +11,18 @@ interface InfoConstituentaProps extends CProps.Div {
function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaProps) { function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaProps) {
return ( return (
<div className={clsx('dense', className)} {...restProps}> <div className={clsx('dense min-w-[15rem]', className)} {...restProps}>
<h2>Конституента {data.alias}</h2> <h2>Конституента {data.alias}</h2>
{data.derived_alias ? ( {data.derived_from_alias ? (
<p> <p>
<b>Основана на: </b> <b>Основание: </b>
{data.derived_alias} {data.derived_from_alias}
</p> </p>
) : null} ) : null}
{data.derived_children.length > 0 ? ( {data.derived_children_alias.length > 0 ? (
<p> <p>
<b>Порождает: </b> <b>Порождает: </b>
{data.derived_children.join(', ')} {data.derived_children_alias.join(', ')}
</p> </p>
) : null} ) : null}
<p> <p>

View File

@ -6,6 +6,7 @@ function HelpConstituenta() {
return ( return (
<div className='dense'> <div className='dense'>
<h1>Редактор конституент</h1> <h1>Редактор конституент</h1>
<p>При выделении также подсвечиваются производные и основание</p>
<p><b>Сохранить изменения</b>: Ctrl + S или клик по кнопке Сохранить</p> <p><b>Сохранить изменения</b>: Ctrl + S или клик по кнопке Сохранить</p>
<p className='mt-1'><b>Формальное определение</b></p> <p className='mt-1'><b>Формальное определение</b></p>
<p>- Ctrl + Пробел дополняет до незанятого имени</p> <p>- Ctrl + Пробел дополняет до незанятого имени</p>

View File

@ -57,7 +57,7 @@ function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps)
/> />
<Checkbox <Checkbox
label='Свернуть производные' label='Свернуть производные'
title='Отображать производные понятия вместе с основным' title='Не отображать производные понятия'
value={params.foldDerived} value={params.foldDerived}
setValue={value => updateParams({ foldDerived: value })} setValue={value => updateParams({ foldDerived: value })}
/> />

View File

@ -97,8 +97,9 @@ export interface IConstituenta extends IConstituentaMeta {
status: ExpressionStatus; status: ExpressionStatus;
is_template: boolean; is_template: boolean;
derived_from: ConstituentaID; derived_from: ConstituentaID;
derived_alias?: string; derived_from_alias?: string;
derived_children: string[]; derived_children: number[];
derived_children_alias: string[];
parse: { parse: {
status: ParsingStatus; status: ParsingStatus;
valueClass: ValueClass; valueClass: ValueClass;

View File

@ -37,6 +37,7 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
derivationLookup.set(cst.id, cst.id); derivationLookup.set(cst.id, cst.id);
cst.derived_from = cst.id; cst.derived_from = cst.id;
cst.derived_children = []; cst.derived_children = [];
cst.derived_children_alias = [];
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass); cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
cst.is_template = inferTemplate(cst.definition_formal); cst.is_template = inferTemplate(cst.definition_formal);
cst.cst_class = inferClass(cst.cst_type, cst.is_template); cst.cst_class = inferClass(cst.cst_type, cst.is_template);
@ -72,8 +73,9 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
if (resolvedInput.size === 1 && isSimpleExpression(definition)) { if (resolvedInput.size === 1 && isSimpleExpression(definition)) {
const parent = result.items.find(item => item.id === resolvedInput.values().next().value)!; const parent = result.items.find(item => item.id === resolvedInput.values().next().value)!;
cst.derived_from = parent.id; cst.derived_from = parent.id;
cst.derived_alias = parent.alias; cst.derived_from_alias = parent.alias;
parent.derived_children.push(cst.alias); parent.derived_children_alias.push(cst.alias);
parent.derived_children.push(cst.id);
derivationLookup.set(cst.id, parent.id); derivationLookup.set(cst.id, parent.id);
} }
}); });
@ -208,6 +210,7 @@ export function createMockConstituenta(id: ConstituentaID, alias: string, commen
id: id, id: id,
derived_from: id, derived_from: id,
derived_children: [], derived_children: [],
derived_children_alias: [],
order: -1, order: -1,
schema: -1, schema: -1,
alias: alias, alias: alias,

View File

@ -19,6 +19,8 @@ const simpleExpressionData = [
['pr1(S1)', 'true'], ['pr1(S1)', 'true'],
['red(S1)', 'true'], ['red(S1)', 'true'],
['red(Pr1(F1[α,σ]))', 'true'], ['red(Pr1(F1[α,σ]))', 'true'],
['(X1)', 'false'],
['(X1)', 'false'],
['D{(α,β)∈D6×D6 | α≠β & α∩β≠∅}', 'false'], ['D{(α,β)∈D6×D6 | α≠β & α∩β≠∅}', 'false'],
['I{(β,α) | α:∈D2; σ:=F5[α]; β:∈σ}', 'false'], ['I{(β,α) | α:∈D2; σ:=F5[α]; β:∈σ}', 'false'],
['∀σ∈S1 (F1[σ]×F1[σ])∩D11=∅', 'false'] ['∀σ∈S1 (F1[σ]×F1[σ])∩D11=∅', 'false']

View File

@ -24,7 +24,7 @@ export function extractGlobals(expression: string): Set<string> {
* Check if expression is simple derivation. * Check if expression is simple derivation.
*/ */
export function isSimpleExpression(text: string): boolean { export function isSimpleExpression(text: string): boolean {
return !COMPLEX_SYMBOLS_REGEXP.test(text); return !text.match(COMPLEX_SYMBOLS_REGEXP);
} }
/** /**

View File

@ -92,7 +92,9 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
result.push({ result.push({
id: String(node.id), id: String(node.id),
fill: colorBgGraphNode(cst, coloringScheme, colors), fill: colorBgGraphNode(cst, coloringScheme, colors),
label: cst.term_resolved && !filterParams.noText ? `${cst.alias}: ${cst.term_resolved}` : cst.alias label: cst.alias,
subLabel: !filterParams.noText ? cst.term_resolved : undefined,
size: cst.derived_from_alias ? 1 : 2
}); });
} }
}); });
@ -164,6 +166,16 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
} }
} }
const handleFoldDerived = useCallback(() => {
setFilterParams(prev => ({
...prev,
foldDerived: !prev.foldDerived
}));
setTimeout(() => {
setToggleResetView(prev => !prev);
}, TIMEOUT_GRAPH_REFRESH);
}, [setFilterParams, setToggleResetView]);
const graph = useMemo( const graph = useMemo(
() => ( () => (
<TermGraph <TermGraph
@ -224,12 +236,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
onDelete={handleDeleteCst} onDelete={handleDeleteCst}
onResetViewpoint={() => setToggleResetView(prev => !prev)} onResetViewpoint={() => setToggleResetView(prev => !prev)}
toggleOrbit={() => setOrbit(prev => !prev)} toggleOrbit={() => setOrbit(prev => !prev)}
toggleFoldDerived={() => toggleFoldDerived={handleFoldDerived}
setFilterParams(prev => ({
...prev,
foldDerived: !prev.foldDerived
}))
}
toggleNoText={() => toggleNoText={() =>
setFilterParams(prev => ({ setFilterParams(prev => ({
...prev, ...prev,

View File

@ -74,7 +74,7 @@ function GraphToolbar({
onClick={toggleNoText} onClick={toggleNoText}
/> />
<MiniButton <MiniButton
title={!foldDerived ? 'Скрыть производные' : 'Отображать производные'} title={!foldDerived ? 'Скрыть производные' : 'Отобразить производные'}
icon={ icon={
!foldDerived ? ( !foldDerived ? (
<IconClustering size='1.25rem' className='icon-green' /> <IconClustering size='1.25rem' className='icon-green' />

View File

@ -45,8 +45,8 @@ function TermGraph({
const { selections, setSelections } = useSelection({ const { selections, setSelections } = useSelection({
ref: graphRef, ref: graphRef,
nodes, nodes: nodes,
edges, edges: edges,
type: 'multi' type: 'multi'
}); });
@ -110,6 +110,8 @@ function TermGraph({
onNodeClick={handleNodeClick} onNodeClick={handleNodeClick}
onNodePointerOver={handleHoverIn} onNodePointerOver={handleHoverIn}
onNodePointerOut={handleHoverOut} onNodePointerOut={handleHoverOut}
minNodeSize={4}
maxNodeSize={8}
cameraMode={orbit ? 'orbit' : is3D ? 'rotate' : 'pan'} cameraMode={orbit ? 'orbit' : is3D ? 'rotate' : 'pan'}
layoutOverrides={ layoutOverrides={
layout.includes('tree') ? { nodeLevelRatio: nodes.length < TREE_SIZE_MILESTONE ? 3 : 1 } : undefined layout.includes('tree') ? { nodeLevelRatio: nodes.length < TREE_SIZE_MILESTONE ? 3 : 1 } : undefined

View File

@ -48,7 +48,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams)
} }
if (params.foldDerived) { if (params.foldDerived) {
schema.items.forEach(cst => { schema.items.forEach(cst => {
if (cst.derived_alias) { if (cst.derived_from_alias) {
graph.foldNode(cst.id); graph.foldNode(cst.id);
} }
}); });

View File

@ -108,6 +108,18 @@ function ConstituentsTable({ items, activeID, onOpenEdit, maxHeight, denseThresh
style: { style: {
backgroundColor: colors.bgSelected backgroundColor: colors.bgSelected
} }
},
{
when: (cst: IConstituenta) => cst.derived_from === activeID && cst.id !== activeID,
style: {
backgroundColor: colors.bgOrange50
}
},
{
when: (cst: IConstituenta) => activeID !== undefined && cst.derived_children.includes(activeID),
style: {
backgroundColor: colors.bgGreen50
}
} }
], ],
[activeID, colors] [activeID, colors]

View File

@ -35,6 +35,9 @@ export interface IColorTheme {
bgTeal: string; bgTeal: string;
bgOrange: string; bgOrange: string;
bgGreen50: string;
bgOrange50: string;
fgRed: string; fgRed: string;
fgGreen: string; fgGreen: string;
fgBlue: string; fgBlue: string;
@ -72,6 +75,9 @@ export const lightT: IColorTheme = {
bgTeal: 'hsl(192, 089%, 081%)', bgTeal: 'hsl(192, 089%, 081%)',
bgOrange: 'hsl(028, 100%, 075%)', bgOrange: 'hsl(028, 100%, 075%)',
bgGreen50: 'hsl(100, 100%, 090%)',
bgOrange50: 'hsl(028, 100%, 095%)',
fgRed: 'hsl(000, 090%, 045%)', fgRed: 'hsl(000, 090%, 045%)',
fgGreen: 'hsl(100, 090%, 035%)', fgGreen: 'hsl(100, 090%, 035%)',
fgBlue: 'hsl(235, 100%, 050%)', fgBlue: 'hsl(235, 100%, 050%)',
@ -109,6 +115,9 @@ export const darkT: IColorTheme = {
bgTeal: 'hsl(192, 080%, 030%)', bgTeal: 'hsl(192, 080%, 030%)',
bgOrange: 'hsl(035, 100%, 035%)', bgOrange: 'hsl(035, 100%, 035%)',
bgGreen50: 'hsl(100, 080%, 017%)',
bgOrange50: 'hsl(035, 100%, 015%)',
fgRed: 'hsl(000, 080%, 045%)', fgRed: 'hsl(000, 080%, 045%)',
fgGreen: 'hsl(100, 080%, 035%)', fgGreen: 'hsl(100, 080%, 035%)',
fgBlue: 'hsl(235, 100%, 080%)', fgBlue: 'hsl(235, 100%, 080%)',