1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
import { IconButton, Size, Type } from '@dash/components';
import { IReactionDisposer, action, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { SettingsManager } from '../../../util/SettingsManager';
import { ButtonType } from '../../nodes/FontIconBox/FontIconBox';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormView.scss';
export interface InfoButton {
targetState?: infoState;
// DocumentOptions fields a button can set
title?: string;
toolTip?: string;
btnType?: ButtonType;
// fields that do not correspond to DocumentOption fields
scripts?: { script?: string; onClick?: string; onDoubleClick?: string };
}
/**
* An Fsa Arc. The first array element is a test condition function that will be observed.
* The second array element is a function that will be invoked when the first test function
* returns a truthy value
*/
export type infoArc = [() => unknown, (res?: unknown) => infoState];
export const StateMessage = Symbol('StateMessage');
export const StateMessageGIF = Symbol('StateMessageGIF');
export const StateEntryFunc = Symbol('StateEntryFunc');
export const StateMessageButton = Symbol('StateMessageButton');
export class infoState {
[StateMessage]: string = '';
[StateMessageGIF]?: string = '';
[StateMessageButton]?: InfoButton[];
[StateEntryFunc]?: () => unknown;
[key: string]: infoArc;
constructor(message: string, arcs?: { [key: string]: infoArc }, messageGif?: string, buttons?: InfoButton[], entryFunc?: () => unknown) {
this[StateMessage] = message;
Object.assign(this, arcs ?? {});
this[StateMessageGIF] = messageGif;
this[StateEntryFunc] = entryFunc;
this[StateMessageButton] = buttons;
}
}
/**
* Create an FSA state.
* @param msg the message displayed when in this state
* @param arcs an object with fields containing @infoArcs (an object with field names indicating the arc transition and
* field values being a tuple of an arc transition trigger function (that returns a truthy value when the arc should fire),
* and an arc transition action function (that sets the next state)
* @param gif the gif displayed when in this state
* @param entryFunc a function to call when entering the state
* @returns an FSA state
*/
export function InfoState(
msg: string, //
arcs?: { [key: string]: infoArc },
gif?: string,
button?: InfoButton[],
entryFunc?: () => unknown
) {
return new infoState(msg, arcs, gif, button, entryFunc);
}
export interface CollectionFreeFormInfoStateProps {
infoState: infoState;
next: (state: infoState) => unknown; // Ensure it's properly defined
close: () => void;
}
@observer
export class CollectionFreeFormInfoState extends ObservableReactComponent<CollectionFreeFormInfoStateProps> {
_disposers: IReactionDisposer[] = [];
@observable _expanded = false;
constructor(props: CollectionFreeFormInfoStateProps) {
super(props);
makeObservable(this);
}
get State() {
return this._props.infoState;
}
set State(value: infoState) {
this._props.infoState = value;
}
get Arcs() {
return Object.keys(this.State ?? []).map(key => this.State?.[key]);
}
clearState = () => this._disposers.map(disposer => disposer());
initState = () => {
this._disposers = this.Arcs
.map(arc => ({ test: arc[0], act: arc[1] }))
.map(arc => reaction(
arc.test,
res => res && this._props.next(arc.act(res)),
{ fireImmediately: true }
)
)}; // prettier-ignore
componentDidMount() {
this.initState();
}
componentDidUpdate(prevProps: Readonly<CollectionFreeFormInfoStateProps>) {
super.componentDidUpdate(prevProps);
this.clearState();
this.initState();
}
componentWillUnmount() {
this.clearState();
}
render() {
const gif = this.State?.[StateMessageGIF];
const buttons = this.State?.[StateMessageButton];
console.log('Rendering CollectionFreeFormInfoState with state:', this.props.infoState);
console.log(buttons);
return (
<div className="collectionFreeform-infoUI">
<p className="collectionFreeform-infoUI-msg">
{this.State?.[StateMessage]}
<button
type="button"
className={'collectionFreeform-' + (!gif ? 'hidden' : 'infoUI-button')}
onClick={action(() => {
this._expanded = !this._expanded;
})}>
{this._expanded ? 'Less...' : 'More...'}
</button>
</p>
<div className={'collectionFreeform-' + (!this._expanded || !gif ? 'hidden' : 'infoUI-gif-container')}>
<img src={`/assets/${gif}`} alt="state message gif" />
</div>
{/* Render the buttons for skipping */}
<div className={'collectionFreeform-' + (!buttons || buttons.length === 0 ? 'hidden' : 'infoUI-button-container')}>
{buttons?.map((button, index) => (
<button
key={index}
type="button"
className="collectionFreeform-infoUI-skip-button"
onClick={action(() => {
console.log('Attempting transition to:', button.targetState);
this.props.next(button.targetState as infoState); // ✅ Use the prop instead
})}>
{button.title}
</button>
))}
</div>
<div className="collectionFreeform-infoUI-close">
<IconButton icon="x" color={SettingsManager.userColor} size={Size.XSMALL} type={Type.TERT} background={SettingsManager.userBackgroundColor} onClick={action(() => this.props.close())} />
</div>
</div>
);
}
}
|