Grabbed dndbeyond's source code
``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
This commit is contained in:
@@ -0,0 +1,433 @@
|
||||
import React from "react";
|
||||
import { connect, DispatchProp } from "react-redux";
|
||||
|
||||
import { Select } from "@dndbeyond/character-components/es";
|
||||
import {
|
||||
characterActions,
|
||||
CharacterValuesContract,
|
||||
Constants,
|
||||
CustomProficiencyContract,
|
||||
EntityRestrictionData,
|
||||
HelperUtils,
|
||||
HtmlSelectOption,
|
||||
HtmlSelectOptionGroup,
|
||||
ProficiencyGroup,
|
||||
RuleData,
|
||||
RuleDataUtils,
|
||||
rulesEngineSelectors,
|
||||
TypeValueLookup,
|
||||
ValueUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { Header } from "~/subApps/sheet/components/Sidebar/components/Header";
|
||||
import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading";
|
||||
|
||||
import ProficiencyGroups from "../../../../CharacterSheet/components/ProficiencyGroups";
|
||||
import { SharedAppState } from "../../../stores/typings";
|
||||
import ProficienciesPaneCustomProficiency from "./ProficienciesPaneCustomProficiency";
|
||||
import ProficienciesPaneExistingProficiency from "./ProficienciesPaneExistingProficiency";
|
||||
|
||||
export const PROFICIENCY_TYPE = {
|
||||
ARMOR: 1,
|
||||
WEAPON: 2,
|
||||
TOOL: 3,
|
||||
LANGUAGE: 4,
|
||||
TOOL_OTHER: 5,
|
||||
LANGUAGE_OTHER: 6,
|
||||
};
|
||||
|
||||
interface Props extends DispatchProp {
|
||||
customProficiencies: Array<CustomProficiencyContract>;
|
||||
proficiencyGroups: Array<ProficiencyGroup>;
|
||||
typeValueLookup: TypeValueLookup;
|
||||
ruleData: RuleData;
|
||||
entityRestrictionData: EntityRestrictionData;
|
||||
}
|
||||
interface State {
|
||||
type: number | null;
|
||||
subtype: number | null;
|
||||
}
|
||||
class ProficienciesPane extends React.PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
type: null,
|
||||
subtype: null,
|
||||
};
|
||||
}
|
||||
|
||||
getSubtypeOptions = (): Array<HtmlSelectOption | HtmlSelectOptionGroup> => {
|
||||
const { type } = this.state;
|
||||
const { ruleData, typeValueLookup, entityRestrictionData } = this.props;
|
||||
|
||||
let valueType: number | null = null;
|
||||
switch (type) {
|
||||
case PROFICIENCY_TYPE.ARMOR:
|
||||
valueType = Constants.AdjustmentTypeEnum.ARMOR_PROFICIENCY_LEVEL;
|
||||
break;
|
||||
case PROFICIENCY_TYPE.WEAPON:
|
||||
valueType = Constants.AdjustmentTypeEnum.WEAPON_PROFICIENCY_LEVEL;
|
||||
break;
|
||||
case PROFICIENCY_TYPE.LANGUAGE:
|
||||
valueType = Constants.AdjustmentTypeEnum.LANGUAGE_PROFICIENCY_LEVEL;
|
||||
break;
|
||||
case PROFICIENCY_TYPE.TOOL:
|
||||
valueType = Constants.AdjustmentTypeEnum.TOOL_PROFICIENCY_LEVEL;
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
|
||||
let options: Array<HtmlSelectOption | HtmlSelectOptionGroup> = [];
|
||||
if (valueType !== null) {
|
||||
let excludeIds = ValueUtils.getTypeValueIntIds(
|
||||
typeValueLookup,
|
||||
valueType
|
||||
);
|
||||
switch (type) {
|
||||
case PROFICIENCY_TYPE.ARMOR:
|
||||
options = RuleDataUtils.getArmorOptions(ruleData, excludeIds);
|
||||
break;
|
||||
case PROFICIENCY_TYPE.WEAPON:
|
||||
options = RuleDataUtils.getWeaponOptions(ruleData, excludeIds);
|
||||
break;
|
||||
case PROFICIENCY_TYPE.LANGUAGE:
|
||||
options = RuleDataUtils.getLanguageOptionsGroupedBySourceCategory(
|
||||
ruleData,
|
||||
excludeIds,
|
||||
entityRestrictionData
|
||||
);
|
||||
break;
|
||||
case PROFICIENCY_TYPE.TOOL:
|
||||
options = RuleDataUtils.getToolOptions(ruleData, excludeIds);
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
getSubtypeNameLookup = (type: number): Record<number, string> => {
|
||||
const { ruleData } = this.props;
|
||||
|
||||
let names: Record<number, string> = {};
|
||||
switch (type) {
|
||||
case Constants.AdjustmentTypeEnum.ARMOR_PROFICIENCY_LEVEL:
|
||||
names = RuleDataUtils.getArmorNameLookup(ruleData);
|
||||
break;
|
||||
case Constants.AdjustmentTypeEnum.WEAPON_PROFICIENCY_LEVEL:
|
||||
names = RuleDataUtils.getWeaponNameLookup(ruleData);
|
||||
break;
|
||||
case Constants.AdjustmentTypeEnum.LANGUAGE_PROFICIENCY_LEVEL:
|
||||
names = RuleDataUtils.getLanguageNameLookup(ruleData);
|
||||
break;
|
||||
case Constants.AdjustmentTypeEnum.TOOL_PROFICIENCY_LEVEL:
|
||||
names = RuleDataUtils.getToolNameLookup(ruleData);
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
return names;
|
||||
};
|
||||
|
||||
handleDataUpdate = (proficiency: CharacterValuesContract): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(
|
||||
characterActions.valueSet(
|
||||
ValueUtils.getTypeId(proficiency),
|
||||
ValueUtils.getValue(proficiency),
|
||||
ValueUtils.getNotes(proficiency),
|
||||
ValueUtils.getValueId(proficiency),
|
||||
ValueUtils.getValueTypeId(proficiency)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
handleProficiencyRemove = (proficiency: CharacterValuesContract): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(
|
||||
characterActions.valueSet(
|
||||
ValueUtils.getTypeId(proficiency),
|
||||
null,
|
||||
null,
|
||||
ValueUtils.getValueId(proficiency),
|
||||
ValueUtils.getValueTypeId(proficiency)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
handleCustomDataUpdate = (
|
||||
id: number,
|
||||
properties: Partial<CustomProficiencyContract>
|
||||
): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.customProficiencySet(id, properties));
|
||||
};
|
||||
|
||||
handleCustomRemove = (proficiency: CustomProficiencyContract): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.customProficiencyRemove(proficiency.id));
|
||||
};
|
||||
|
||||
handleCustomProficiencyAdd = (type: number): void => {
|
||||
const { dispatch, customProficiencies } = this.props;
|
||||
|
||||
let label: string = "Proficiency";
|
||||
let customType: number | null = null;
|
||||
switch (type) {
|
||||
case PROFICIENCY_TYPE.LANGUAGE_OTHER:
|
||||
label = "Language";
|
||||
customType = Constants.CustomProficiencyTypeEnum.LANGUAGE;
|
||||
break;
|
||||
case PROFICIENCY_TYPE.TOOL_OTHER:
|
||||
label = "Tool";
|
||||
customType = Constants.CustomProficiencyTypeEnum.TOOL;
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
|
||||
if (customType !== null) {
|
||||
dispatch(
|
||||
characterActions.customProficiencyCreate(
|
||||
`Custom ${label} ${customProficiencies.length + 1}`,
|
||||
customType
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
handleTypeChange = (type: string): void => {
|
||||
let typeValue = HelperUtils.parseInputInt(type);
|
||||
switch (typeValue) {
|
||||
case PROFICIENCY_TYPE.LANGUAGE_OTHER:
|
||||
case PROFICIENCY_TYPE.TOOL_OTHER:
|
||||
this.handleCustomProficiencyAdd(typeValue);
|
||||
this.setState({
|
||||
type: null,
|
||||
});
|
||||
break;
|
||||
case PROFICIENCY_TYPE.ARMOR:
|
||||
case PROFICIENCY_TYPE.WEAPON:
|
||||
case PROFICIENCY_TYPE.LANGUAGE:
|
||||
case PROFICIENCY_TYPE.TOOL:
|
||||
default:
|
||||
this.setState({
|
||||
type: typeValue,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleSubtypeChange = (subtype: string): void => {
|
||||
const { type } = this.state;
|
||||
const { dispatch, ruleData } = this.props;
|
||||
|
||||
let subtypeValue = HelperUtils.parseInputInt(subtype);
|
||||
|
||||
let valueType: number | null = null;
|
||||
let valueId: number | null = null;
|
||||
let valueTypeId: number | null = null;
|
||||
switch (type) {
|
||||
case PROFICIENCY_TYPE.ARMOR:
|
||||
valueType = Constants.AdjustmentTypeEnum.ARMOR_PROFICIENCY_LEVEL;
|
||||
valueId = subtypeValue;
|
||||
valueTypeId = Constants.ItemBaseTypeIdEnum.ARMOR;
|
||||
break;
|
||||
case PROFICIENCY_TYPE.WEAPON:
|
||||
valueType = Constants.AdjustmentTypeEnum.WEAPON_PROFICIENCY_LEVEL;
|
||||
valueId = subtypeValue;
|
||||
valueTypeId = Constants.ItemBaseTypeIdEnum.WEAPON;
|
||||
break;
|
||||
case PROFICIENCY_TYPE.LANGUAGE:
|
||||
valueType = Constants.AdjustmentTypeEnum.LANGUAGE_PROFICIENCY_LEVEL;
|
||||
valueId = subtypeValue;
|
||||
valueTypeId = RuleDataUtils.getLanguageTypeId(ruleData);
|
||||
break;
|
||||
case PROFICIENCY_TYPE.TOOL:
|
||||
valueType = Constants.AdjustmentTypeEnum.TOOL_PROFICIENCY_LEVEL;
|
||||
valueId = subtypeValue;
|
||||
valueTypeId = RuleDataUtils.getToolTypeId(ruleData);
|
||||
break;
|
||||
}
|
||||
|
||||
if (type !== null && valueType !== null) {
|
||||
dispatch(
|
||||
characterActions.valueSet(
|
||||
valueType,
|
||||
Constants.ProficiencyLevelEnum.FULL,
|
||||
null,
|
||||
ValueUtils.hack__toString(valueId),
|
||||
ValueUtils.hack__toString(valueTypeId)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderCustomProficiencies = (): React.ReactNode => {
|
||||
const { typeValueLookup, customProficiencies, ruleData } = this.props;
|
||||
|
||||
const proficiencyGroupData = RuleDataUtils.getProficiencyGroups(ruleData);
|
||||
|
||||
return (
|
||||
<div className="ct-proficiencies-pane__customs">
|
||||
{proficiencyGroupData.map((group) => {
|
||||
let proficiencies: Array<CharacterValuesContract> = [];
|
||||
let custom: Array<CustomProficiencyContract> = [];
|
||||
|
||||
if (group.customAdjustments !== null) {
|
||||
group.customAdjustments.forEach((typeId) => {
|
||||
let charValues = ValueUtils.getTypeData(typeValueLookup, typeId);
|
||||
proficiencies.push(...charValues);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO get real data
|
||||
switch (group.label) {
|
||||
case "Tools":
|
||||
custom = customProficiencies.filter(
|
||||
(customProficiency) =>
|
||||
customProficiency.type ===
|
||||
Constants.CustomProficiencyTypeEnum.TOOL
|
||||
);
|
||||
break;
|
||||
case "Languages":
|
||||
custom = customProficiencies.filter(
|
||||
(customProficiency) =>
|
||||
customProficiency.type ===
|
||||
Constants.CustomProficiencyTypeEnum.LANGUAGE
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
|
||||
if (!proficiencies.length && !custom.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="ct-proficiencies-pane__custom"
|
||||
key={group.label ? group.label : ""}
|
||||
>
|
||||
<div className="ct-proficiencies-pane__custom-label">
|
||||
<Heading>{group.label}</Heading>
|
||||
</div>
|
||||
<div className="ct-proficiencies-pane__proficiencies">
|
||||
{proficiencies.map((proficiency) => {
|
||||
return (
|
||||
<ProficienciesPaneExistingProficiency
|
||||
key={ValueUtils.getUniqueKey(proficiency)}
|
||||
proficiency={proficiency}
|
||||
nameLookup={this.getSubtypeNameLookup(
|
||||
ValueUtils.getTypeId(proficiency)
|
||||
)}
|
||||
onRemove={this.handleProficiencyRemove}
|
||||
onDataUpdate={this.handleDataUpdate}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{custom.map((proficiency) => (
|
||||
<ProficienciesPaneCustomProficiency
|
||||
key={proficiency.id}
|
||||
proficiency={proficiency}
|
||||
onRemove={this.handleCustomRemove}
|
||||
onDataUpdate={this.handleCustomDataUpdate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderAdd = (): React.ReactNode => {
|
||||
const { type, subtype } = this.state;
|
||||
|
||||
let typeOptions: Array<HtmlSelectOptionGroup> = [
|
||||
{
|
||||
optGroupLabel: "Existing",
|
||||
options: [
|
||||
{ label: "Armor", value: PROFICIENCY_TYPE.ARMOR },
|
||||
{ label: "Weapon", value: PROFICIENCY_TYPE.WEAPON },
|
||||
{ label: "Tool", value: PROFICIENCY_TYPE.TOOL },
|
||||
{ label: "Language", value: PROFICIENCY_TYPE.LANGUAGE },
|
||||
],
|
||||
},
|
||||
{
|
||||
optGroupLabel: "Custom",
|
||||
resetAfterChoice: true,
|
||||
options: [
|
||||
{ label: "Tool", value: PROFICIENCY_TYPE.TOOL_OTHER },
|
||||
{ label: "Language", value: PROFICIENCY_TYPE.LANGUAGE_OTHER },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let subtypeOptions = this.getSubtypeOptions();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Heading>Add New Proficiencies</Heading>
|
||||
<div className="ct-proficiencies-pane__add">
|
||||
<div className="ct-proficiencies-pane__add-field ct-proficiencies-pane__add-field--type">
|
||||
<Select
|
||||
options={typeOptions}
|
||||
onChange={this.handleTypeChange}
|
||||
value={type}
|
||||
/>
|
||||
</div>
|
||||
{subtypeOptions.length > 0 && (
|
||||
<div className="ct-proficiencies-pane__add-field ct-proficiencies-pane__add-field--subtype">
|
||||
<Select
|
||||
options={subtypeOptions}
|
||||
onChange={this.handleSubtypeChange}
|
||||
value={subtype}
|
||||
initialOptionRemoved={true}
|
||||
resetAfterChoice={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
renderProficiencyGroups = (): React.ReactNode => {
|
||||
const { proficiencyGroups } = this.props;
|
||||
|
||||
return (
|
||||
<div className="ct-proficiencies-pane__groups">
|
||||
<ProficiencyGroups proficiencyGroups={proficiencyGroups} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ct-proficiencies-pane">
|
||||
<Header>Proficiencies & Training</Header>
|
||||
{this.renderCustomProficiencies()}
|
||||
{this.renderAdd()}
|
||||
{this.renderProficiencyGroups()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: SharedAppState) {
|
||||
return {
|
||||
customProficiencies: rulesEngineSelectors.getCustomProficiencies(state),
|
||||
typeValueLookup: rulesEngineSelectors.getCharacterValueLookupByType(state),
|
||||
ruleData: rulesEngineSelectors.getRuleData(state),
|
||||
proficiencyGroups: rulesEngineSelectors.getProficiencyGroups(state),
|
||||
entityRestrictionData: rulesEngineSelectors.getEntityRestrictionData(state),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ProficienciesPane);
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import React from "react";
|
||||
|
||||
import { CustomProficiencyContract } from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { RemoveButton } from "../../../../components/common/Button";
|
||||
import ProficienciesPaneProficiencyEditor from "../ProficienciesPaneProficiencyEditor";
|
||||
|
||||
interface Props {
|
||||
proficiency: CustomProficiencyContract;
|
||||
onDataUpdate?: (id: number, data: Record<string, any>) => void;
|
||||
onRemove?: (proficiency: CustomProficiencyContract) => void;
|
||||
}
|
||||
export default class ProficienciesPaneCustomProficiency extends React.PureComponent<Props> {
|
||||
getData = (): Record<string, any> => {
|
||||
const { proficiency } = this.props;
|
||||
|
||||
if (proficiency === null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return proficiency;
|
||||
};
|
||||
|
||||
handleDataUpdate = (propertyKey: string, value: string): void => {
|
||||
const { onDataUpdate, proficiency } = this.props;
|
||||
|
||||
if (onDataUpdate) {
|
||||
let newProperties = this.getData();
|
||||
onDataUpdate(proficiency.id, {
|
||||
...newProperties,
|
||||
[propertyKey]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleRemove = (): void => {
|
||||
const { onRemove, proficiency } = this.props;
|
||||
|
||||
if (onRemove) {
|
||||
onRemove(proficiency);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { proficiency } = this.props;
|
||||
|
||||
return (
|
||||
<div className="ct-proficiencies-pane__proficiency">
|
||||
<div className="ct-proficiencies-pane__proficiency-primary">
|
||||
<div className="ct-proficiencies-pane__proficiency-name">
|
||||
<ProficienciesPaneProficiencyEditor
|
||||
onUpdate={this.handleDataUpdate}
|
||||
defaultValue={proficiency.name}
|
||||
propertyKey="name"
|
||||
placeholder="Enter Name"
|
||||
/>
|
||||
</div>
|
||||
<div className="ct-proficiencies-pane__proficiency-actions">
|
||||
<div className="ct-proficiencies-pane__proficiency-action ct-proficiencies-pane__proficiency-action--remove">
|
||||
<RemoveButton onClick={this.handleRemove}>
|
||||
Remove Proficiency
|
||||
</RemoveButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ct-proficiencies-pane__proficiency-notes">
|
||||
<ProficienciesPaneProficiencyEditor
|
||||
onUpdate={this.handleDataUpdate}
|
||||
defaultValue={proficiency.notes}
|
||||
propertyKey="notes"
|
||||
placeholder="Enter Source Note..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import ProficienciesPaneCustomProficiency from "./ProficienciesPaneCustomProficiency";
|
||||
|
||||
export default ProficienciesPaneCustomProficiency;
|
||||
export { ProficienciesPaneCustomProficiency };
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
CharacterValuesContract,
|
||||
ValueUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { RemoveButton } from "../../../../components/common/Button";
|
||||
import ProficienciesPaneProficiencyEditor from "../ProficienciesPaneProficiencyEditor";
|
||||
|
||||
interface Props {
|
||||
proficiency: CharacterValuesContract;
|
||||
nameLookup: Record<number, string>;
|
||||
onDataUpdate: (proficiency: CharacterValuesContract) => void;
|
||||
onRemove: (proficiency: CharacterValuesContract) => void;
|
||||
}
|
||||
interface State {
|
||||
proficiency: CharacterValuesContract;
|
||||
}
|
||||
export default class ProficienciesPaneExistingProficiency extends React.PureComponent<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = this.generateStateData(props);
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
prevProps: Readonly<Props>,
|
||||
prevState: Readonly<State>
|
||||
): void {
|
||||
const { proficiency } = this.props;
|
||||
|
||||
if (proficiency !== prevProps.proficiency) {
|
||||
this.setState(this.generateStateData(this.props));
|
||||
}
|
||||
}
|
||||
|
||||
generateStateData = (props: Props): State => {
|
||||
const { proficiency } = props;
|
||||
|
||||
return {
|
||||
proficiency,
|
||||
};
|
||||
};
|
||||
|
||||
handleDataUpdate = (propertyKey: string, value) => {
|
||||
const { onDataUpdate } = this.props;
|
||||
|
||||
this.setState((prevState) => {
|
||||
let newProficiency = {
|
||||
...prevState.proficiency,
|
||||
[propertyKey]: value,
|
||||
};
|
||||
|
||||
if (onDataUpdate) {
|
||||
onDataUpdate(newProficiency);
|
||||
}
|
||||
|
||||
return {
|
||||
proficiency: newProficiency,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
handleRemove = () => {
|
||||
const { proficiency } = this.state;
|
||||
const { onRemove } = this.props;
|
||||
|
||||
if (onRemove) {
|
||||
onRemove(proficiency);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { proficiency } = this.state;
|
||||
const { nameLookup } = this.props;
|
||||
|
||||
const valueId = ValueUtils.getValueId(proficiency);
|
||||
return (
|
||||
<div className="ct-proficiencies-pane__proficiency">
|
||||
<div className="ct-proficiencies-pane__proficiency-primary">
|
||||
<div className="ct-proficiencies-pane__proficiency-name">
|
||||
{valueId === null ? "" : nameLookup[valueId]}
|
||||
</div>
|
||||
<div className="ct-proficiencies-pane__proficiency-actions">
|
||||
<div className="ct-proficiencies-pane__proficiency-action ct-proficiencies-pane__proficiency-action--remove">
|
||||
<RemoveButton onClick={this.handleRemove}>
|
||||
Remove Proficiency
|
||||
</RemoveButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ct-proficiencies-pane__proficiency-notes">
|
||||
<ProficienciesPaneProficiencyEditor
|
||||
onUpdate={this.handleDataUpdate}
|
||||
defaultValue={ValueUtils.getNotes(proficiency)}
|
||||
propertyKey="notes"
|
||||
placeholder="Enter Source Note..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import ProficienciesPaneExistingProficiency from "./ProficienciesPaneExistingProficiency";
|
||||
|
||||
export default ProficienciesPaneExistingProficiency;
|
||||
export { ProficienciesPaneExistingProficiency };
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
label?: string;
|
||||
propertyKey: string;
|
||||
defaultValue: string | null;
|
||||
onUpdate?: (propertyKey: string, value: string) => void;
|
||||
placeholder: string;
|
||||
}
|
||||
export default class ProficienciesPaneProficiencyEditor extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
placeholder: "",
|
||||
};
|
||||
|
||||
handleBlur = (evt: React.FocusEvent<HTMLInputElement>): void => {
|
||||
const { propertyKey, onUpdate } = this.props;
|
||||
|
||||
if (onUpdate) {
|
||||
onUpdate(propertyKey, evt.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { label, defaultValue, placeholder } = this.props;
|
||||
|
||||
return (
|
||||
<div className="ct-proficiencies-pane__proficiency-editor">
|
||||
<div className="ct-proficiencies-pane__proficiency-editor-field">
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={defaultValue === null ? "" : defaultValue}
|
||||
onBlur={this.handleBlur}
|
||||
className="ct-proficiencies-pane__proficiency-editor-input"
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</div>
|
||||
{label && (
|
||||
<div className="ct-proficiencies-pane__proficiency-editor-label">
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import ProficienciesPaneProficiencyEditor from "./ProficienciesPaneProficiencyEditor";
|
||||
|
||||
export default ProficienciesPaneProficiencyEditor;
|
||||
export { ProficienciesPaneProficiencyEditor };
|
||||
@@ -0,0 +1,12 @@
|
||||
import ProficienciesPane from "./ProficienciesPane";
|
||||
import ProficienciesPaneCustomProficiency from "./ProficienciesPaneCustomProficiency";
|
||||
import ProficienciesPaneExistingProficiency from "./ProficienciesPaneExistingProficiency";
|
||||
import ProficienciesPaneProficiencyEditor from "./ProficienciesPaneProficiencyEditor";
|
||||
|
||||
export default ProficienciesPane;
|
||||
export {
|
||||
ProficienciesPane,
|
||||
ProficienciesPaneCustomProficiency,
|
||||
ProficienciesPaneExistingProficiency,
|
||||
ProficienciesPaneProficiencyEditor,
|
||||
};
|
||||
Reference in New Issue
Block a user