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,562 @@
|
||||
import React from "react";
|
||||
import { connect, DispatchProp } from "react-redux";
|
||||
|
||||
import { PrivacyTypeRadio } from "@dndbeyond/character-components/es";
|
||||
import {
|
||||
AnySimpleDataType,
|
||||
characterActions,
|
||||
CharacterPreferences,
|
||||
CharacterTheme,
|
||||
CharacterUtils,
|
||||
CharClass,
|
||||
ClassSpellListSpellsLookup,
|
||||
ClassUtils,
|
||||
Constants,
|
||||
DataOriginRefData,
|
||||
HelperUtils,
|
||||
HtmlSelectOption,
|
||||
Race,
|
||||
RuleData,
|
||||
RuleDataUtils,
|
||||
rulesEngineSelectors,
|
||||
RaceUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
import { Dice } from "@dndbeyond/dice";
|
||||
|
||||
import { SourceCategoryDescription } from "~/constants";
|
||||
import { useSidebar } from "~/contexts/Sidebar";
|
||||
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
|
||||
import { Header } from "~/subApps/sheet/components/Sidebar/components/Header";
|
||||
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";
|
||||
|
||||
import config from "../../../../config";
|
||||
import { appEnvActions } from "../../../actions";
|
||||
import {
|
||||
CheckboxInfo,
|
||||
FormCheckBoxesField,
|
||||
} from "../../../components/common/FormCheckBoxesField";
|
||||
import { appEnvSelectors } from "../../../selectors";
|
||||
import { SharedAppState } from "../../../stores/typings";
|
||||
import { PaneIdentifierUtils } from "../../../utils";
|
||||
import PreferencesPaneSelectField from "./PreferencesPaneSelectField";
|
||||
import { ToggleInfo } from "./PreferencesPaneTogglesField";
|
||||
|
||||
interface Props extends DispatchProp {
|
||||
activeSourceCategories: Array<number>;
|
||||
preferences: CharacterPreferences;
|
||||
ruleData: RuleData;
|
||||
diceEnabled: boolean;
|
||||
classes: Array<CharClass>;
|
||||
species: Race | null;
|
||||
dataOriginRefData: DataOriginRefData;
|
||||
classSpellListSpellsLookup: ClassSpellListSpellsLookup;
|
||||
characterTheme: CharacterTheme;
|
||||
paneHistoryStart: PaneInfo["paneHistoryStart"];
|
||||
}
|
||||
class PreferencesPane extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
diceEnabled: false,
|
||||
};
|
||||
|
||||
handlePreferenceChange = (
|
||||
prefKey: string,
|
||||
value: AnySimpleDataType
|
||||
): void => {
|
||||
const { dispatch } = this.props;
|
||||
const typedPrefKey = CharacterUtils.getPreferenceKey(prefKey);
|
||||
if (typedPrefKey !== null) {
|
||||
dispatch(characterActions.preferenceChoose(typedPrefKey, value));
|
||||
}
|
||||
};
|
||||
|
||||
handleIntPreferenceChange = (prefKey: string, value: number | null): void => {
|
||||
const { dispatch } = this.props;
|
||||
const typedPrefKey = CharacterUtils.getPreferenceKey(prefKey);
|
||||
if (typedPrefKey !== null) {
|
||||
dispatch(characterActions.preferenceChoose(typedPrefKey, value));
|
||||
}
|
||||
};
|
||||
|
||||
handleSourceCategoryChange = (sourceId: number, isActive: boolean): void => {
|
||||
const { dispatch, activeSourceCategories } = this.props;
|
||||
let newSourceCats: Array<number> = [];
|
||||
if (isActive) {
|
||||
newSourceCats = [...activeSourceCategories, sourceId];
|
||||
} else {
|
||||
newSourceCats = activeSourceCategories.filter((id) => id !== sourceId);
|
||||
}
|
||||
|
||||
dispatch(characterActions.activeSourceCategoriesSet(newSourceCats));
|
||||
};
|
||||
|
||||
handleOptionalClassFeaturesPreferenceChangePromise = (
|
||||
newIsEnabled: boolean,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
): void => {
|
||||
const { paneHistoryStart, classes, classSpellListSpellsLookup } =
|
||||
this.props;
|
||||
const spellListIds: Array<number> =
|
||||
ClassUtils.getUpdateEnableOptionalClassFeaturesSpellListIdsToRemove(
|
||||
classes,
|
||||
newIsEnabled
|
||||
);
|
||||
|
||||
const hasSpellsToRemove = spellListIds.some((id) =>
|
||||
classSpellListSpellsLookup.hasOwnProperty(id)
|
||||
);
|
||||
|
||||
if (!hasSpellsToRemove) {
|
||||
this.handlePreferenceChange("enableOptionalClassFeatures", newIsEnabled);
|
||||
accept();
|
||||
} else {
|
||||
paneHistoryStart(
|
||||
PaneComponentEnum.PREFERENCES_OPTIONAL_CLASS_FEATURES_CONFIRM,
|
||||
PaneIdentifierUtils.generatePreferenceOptionalClassFeaturesConfirm(
|
||||
spellListIds,
|
||||
newIsEnabled
|
||||
)
|
||||
);
|
||||
reject();
|
||||
}
|
||||
};
|
||||
|
||||
handleOptionalOriginsPreferenceChangePromise = (
|
||||
newIsEnabled: boolean,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
): void => {
|
||||
const { paneHistoryStart, species, classSpellListSpellsLookup } =
|
||||
this.props;
|
||||
|
||||
if (!species) {
|
||||
this.handlePreferenceChange("enableOptionalOrigins", newIsEnabled);
|
||||
accept();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const spellListIds: Array<number> =
|
||||
RaceUtils.getUpdateEnableOptionalOriginsSpellListIdsToRemove(
|
||||
species,
|
||||
newIsEnabled
|
||||
);
|
||||
|
||||
const hasSpellsToRemove = spellListIds.some((id) =>
|
||||
classSpellListSpellsLookup.hasOwnProperty(id)
|
||||
);
|
||||
|
||||
if (!hasSpellsToRemove) {
|
||||
this.handlePreferenceChange("enableOptionalOrigins", newIsEnabled);
|
||||
accept();
|
||||
} else {
|
||||
paneHistoryStart(
|
||||
PaneComponentEnum.PREFERENCES_OPTIONAL_ORIGINS_CONFIRM,
|
||||
PaneIdentifierUtils.generatePreferenceOptionalOriginsConfirm(
|
||||
spellListIds,
|
||||
newIsEnabled
|
||||
)
|
||||
);
|
||||
reject();
|
||||
}
|
||||
};
|
||||
|
||||
handleProgressionPreferenceChangePromise = (
|
||||
newId: string,
|
||||
oldId: string,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
): void => {
|
||||
const { paneHistoryStart } = this.props;
|
||||
|
||||
const newIdValue = HelperUtils.parseInputInt(newId);
|
||||
|
||||
if (newIdValue !== null) {
|
||||
paneHistoryStart(
|
||||
PaneComponentEnum.PREFERENCES_PROGRESSION_CONFIRM,
|
||||
PaneIdentifierUtils.generatePreferenceProgressionConfirm(newIdValue)
|
||||
);
|
||||
}
|
||||
|
||||
reject();
|
||||
};
|
||||
|
||||
handleHitPointPreferenceChangePromise = (
|
||||
newId: string,
|
||||
oldId: string,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
): void => {
|
||||
const { paneHistoryStart } = this.props;
|
||||
|
||||
const newIdValue = HelperUtils.parseInputInt(newId);
|
||||
|
||||
if (newIdValue !== null) {
|
||||
paneHistoryStart(
|
||||
PaneComponentEnum.PREFERENCES_HIT_POINT_CONFIRM,
|
||||
PaneIdentifierUtils.generatePreferenceHitPointConfirm(newIdValue)
|
||||
);
|
||||
}
|
||||
|
||||
reject();
|
||||
};
|
||||
|
||||
handleDiceToggle = (): void => {
|
||||
const { dispatch, diceEnabled } = this.props;
|
||||
|
||||
const newDiceEnabledSetting: boolean = !diceEnabled;
|
||||
|
||||
try {
|
||||
localStorage.setItem("dice-enabled", newDiceEnabledSetting.toString());
|
||||
Dice.setEnabled(newDiceEnabledSetting);
|
||||
} catch (e) {}
|
||||
|
||||
dispatch(
|
||||
appEnvActions.dataSet({
|
||||
diceEnabled: newDiceEnabledSetting,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
handleChangePrivacy = (value: number | null): void => {
|
||||
const typedPrefKey = CharacterUtils.getPreferenceKey("privacyType");
|
||||
if (typedPrefKey !== null) {
|
||||
this.props.dispatch(
|
||||
characterActions.preferenceChoose(typedPrefKey, value)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { preferences, activeSourceCategories, ruleData, diceEnabled } =
|
||||
this.props;
|
||||
|
||||
const {
|
||||
useHomebrewContent,
|
||||
encumbranceType,
|
||||
hitPointType,
|
||||
progressionType,
|
||||
abilityScoreDisplayType,
|
||||
sharingType,
|
||||
privacyType,
|
||||
ignoreCoinWeight,
|
||||
enforceFeatRules,
|
||||
enforceMulticlassRules,
|
||||
showScaledSpells,
|
||||
enableOptionalClassFeatures,
|
||||
enableOptionalOrigins,
|
||||
enableDarkMode,
|
||||
} = preferences;
|
||||
const sourceCategories = RuleDataUtils.getSourceCategories(ruleData);
|
||||
|
||||
const encumbranceOptions: Array<HtmlSelectOption> = [
|
||||
{
|
||||
label: "Use Encumbrance",
|
||||
value: Constants.PreferenceEncumbranceTypeEnum.ENCUMBRANCE,
|
||||
},
|
||||
{
|
||||
label: "No Encumbrance",
|
||||
value: Constants.PreferenceEncumbranceTypeEnum.NONE,
|
||||
},
|
||||
{
|
||||
label: "Variant Encumbrance",
|
||||
value: Constants.PreferenceEncumbranceTypeEnum.VARIANT,
|
||||
},
|
||||
];
|
||||
|
||||
const hpOptions: Array<HtmlSelectOption> = [
|
||||
{ label: "Fixed", value: Constants.PreferenceHitPointTypeEnum.FIXED },
|
||||
{ label: "Manual", value: Constants.PreferenceHitPointTypeEnum.MANUAL },
|
||||
];
|
||||
|
||||
const advancementOptions: Array<HtmlSelectOption> = [
|
||||
{
|
||||
label: "Milestone",
|
||||
value: Constants.PreferenceProgressionTypeEnum.MILESTONE,
|
||||
},
|
||||
{ label: "XP", value: Constants.PreferenceProgressionTypeEnum.XP },
|
||||
];
|
||||
|
||||
const abilityDisplayOptions: Array<HtmlSelectOption> = [
|
||||
{
|
||||
label: "Modifiers Top",
|
||||
value: Constants.PreferenceAbilityScoreDisplayTypeEnum.MODIFIERS_TOP,
|
||||
},
|
||||
{
|
||||
label: "Scores Top",
|
||||
value: Constants.PreferenceAbilityScoreDisplayTypeEnum.SCORES_TOP,
|
||||
},
|
||||
];
|
||||
|
||||
const sharingOptions: Array<HtmlSelectOption> = [
|
||||
{ label: "Full", value: Constants.PreferenceSharingTypeEnum.FULL },
|
||||
{ label: "Limited", value: Constants.PreferenceSharingTypeEnum.LIMITED },
|
||||
// {label: 'Stat Block', value: Constants.PreferenceSharingTypeEnum.STAT_BLOCK},
|
||||
];
|
||||
|
||||
let sourceToggles: Array<CheckboxInfo> = [];
|
||||
let partneredSourceToggles: Array<CheckboxInfo> = [];
|
||||
|
||||
sourceCategories.forEach((sourceCategory) => {
|
||||
if (!sourceCategory.isToggleable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const toggle: ToggleInfo = {
|
||||
label: `${sourceCategory.name}`,
|
||||
initiallyEnabled: activeSourceCategories.includes(sourceCategory.id),
|
||||
onChange: this.handleSourceCategoryChange.bind(this, sourceCategory.id),
|
||||
sortOrder: sourceCategory.sortOrder,
|
||||
};
|
||||
|
||||
if (sourceCategory.isPartneredContent) {
|
||||
partneredSourceToggles.push(toggle);
|
||||
} else {
|
||||
sourceToggles.push(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="ct-preferences-pane">
|
||||
<Header>Preferences</Header>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Sources"
|
||||
description={SourceCategoryDescription.official}
|
||||
checkboxes={[
|
||||
{
|
||||
label: "Homebrew",
|
||||
initiallyEnabled: useHomebrewContent,
|
||||
onChange: this.handlePreferenceChange.bind(
|
||||
this,
|
||||
"useHomebrewContent"
|
||||
),
|
||||
sortOrder: 0,
|
||||
},
|
||||
...sourceToggles,
|
||||
]}
|
||||
themed={true}
|
||||
showAccordion={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Partnered Content"
|
||||
description={SourceCategoryDescription.partnered}
|
||||
checkboxes={partneredSourceToggles}
|
||||
checkUncheckAllEnabled={true}
|
||||
themed={true}
|
||||
accordionHeading="Categories"
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
showAccordion={true}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Underdark Mode"
|
||||
description="Enables dark mode for this character"
|
||||
checkboxes={[
|
||||
{
|
||||
initiallyEnabled: enableDarkMode,
|
||||
label: "Enable dark mode",
|
||||
onChange: this.handlePreferenceChange.bind(
|
||||
this,
|
||||
"enableDarkMode"
|
||||
),
|
||||
},
|
||||
]}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Dice Rolling"
|
||||
description="Enables digital dice rolling for this character"
|
||||
checkboxes={[
|
||||
{
|
||||
initiallyEnabled: diceEnabled,
|
||||
label: "Enable Dice Rolling",
|
||||
onChange: this.handleDiceToggle,
|
||||
},
|
||||
]}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Optional Features"
|
||||
description="Allow or restrict optional features for this character."
|
||||
checkboxes={[
|
||||
{
|
||||
label: "Optional Class Features",
|
||||
initiallyEnabled: enableOptionalClassFeatures,
|
||||
onChangePromise:
|
||||
this.handleOptionalClassFeaturesPreferenceChangePromise,
|
||||
},
|
||||
{
|
||||
label: "Customize Your Origin",
|
||||
initiallyEnabled: enableOptionalOrigins,
|
||||
onChangePromise:
|
||||
this.handleOptionalOriginsPreferenceChangePromise,
|
||||
},
|
||||
]}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<PreferencesPaneSelectField
|
||||
heading="Advancement Type"
|
||||
description="Story-based character progression / XP-based character progression"
|
||||
onChangePromise={this.handleProgressionPreferenceChangePromise}
|
||||
initialOptionRemoved={true}
|
||||
options={advancementOptions}
|
||||
initialValue={progressionType}
|
||||
block={true}
|
||||
/>
|
||||
<PreferencesPaneSelectField
|
||||
heading="Hit Point Type"
|
||||
description="When leveling up, increase hit points by the fixed value for your chosen class or manually enter a rolled value"
|
||||
onChangePromise={this.handleHitPointPreferenceChangePromise}
|
||||
initialOptionRemoved={true}
|
||||
options={hpOptions}
|
||||
initialValue={hitPointType}
|
||||
block={true}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Use Prerequisites"
|
||||
description="Allow or restrict choices based on rule prerequisites for the following for this character"
|
||||
checkboxes={[
|
||||
{
|
||||
label: "Feats",
|
||||
initiallyEnabled: enforceFeatRules,
|
||||
onChange: this.handlePreferenceChange.bind(
|
||||
this,
|
||||
"enforceFeatRules"
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Multiclass Requirements",
|
||||
initiallyEnabled: enforceMulticlassRules,
|
||||
onChange: this.handlePreferenceChange.bind(
|
||||
this,
|
||||
"enforceMulticlassRules"
|
||||
),
|
||||
},
|
||||
]}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Show level-scaled spells"
|
||||
description="Display and highlight available spells to cast with higher level spell slots"
|
||||
checkboxes={[
|
||||
{
|
||||
initiallyEnabled: showScaledSpells,
|
||||
label: "Show Level-Scaled Spells",
|
||||
onChange: this.handlePreferenceChange.bind(
|
||||
this,
|
||||
"showScaledSpells"
|
||||
),
|
||||
},
|
||||
]}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<PreferencesPaneSelectField
|
||||
heading="Encumbrance Type"
|
||||
description="Use the standard encumbrance rules / Disable the encumbrance display / Use the more detailed rules for encumbrance"
|
||||
onChange={this.handleIntPreferenceChange.bind(
|
||||
this,
|
||||
"encumbranceType"
|
||||
)}
|
||||
initialOptionRemoved={true}
|
||||
options={encumbranceOptions}
|
||||
initialValue={encumbranceType}
|
||||
block={true}
|
||||
/>
|
||||
|
||||
<FormCheckBoxesField
|
||||
heading="Ignore Coin Weight"
|
||||
description="Coins do not count against your total weight carried (50 coins weigh 1 lb.)"
|
||||
checkboxes={[
|
||||
{
|
||||
initiallyEnabled: ignoreCoinWeight,
|
||||
label: "Ignore Coin Weight",
|
||||
onChange: this.handlePreferenceChange.bind(
|
||||
this,
|
||||
"ignoreCoinWeight"
|
||||
),
|
||||
},
|
||||
]}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
darkMode={enableDarkMode}
|
||||
/>
|
||||
|
||||
<PreferencesPaneSelectField
|
||||
heading="Ability Score/Modifier Display"
|
||||
description="Reverse the arrangement of ability modifiers and scores"
|
||||
onChange={this.handleIntPreferenceChange.bind(
|
||||
this,
|
||||
"abilityScoreDisplayType"
|
||||
)}
|
||||
initialOptionRemoved={true}
|
||||
options={abilityDisplayOptions}
|
||||
initialValue={abilityScoreDisplayType}
|
||||
block={true}
|
||||
/>
|
||||
|
||||
<PrivacyTypeRadio
|
||||
handleChange={(e) =>
|
||||
this.handleChangePrivacy(parseInt(e?.target?.value))
|
||||
}
|
||||
darkMode={enableDarkMode}
|
||||
initialValue={privacyType}
|
||||
themed={true}
|
||||
variant="sidebar"
|
||||
/>
|
||||
|
||||
<div className="ct-preferences-pane__version">
|
||||
<div className="ct-preferences-pane__version-label">Version:</div>
|
||||
<div className="ct-preferences-pane__version-value">
|
||||
{config.version}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: SharedAppState) {
|
||||
return {
|
||||
ruleData: rulesEngineSelectors.getRuleData(state),
|
||||
preferences: rulesEngineSelectors.getCharacterPreferences(state),
|
||||
activeSourceCategories:
|
||||
rulesEngineSelectors.getActiveSourceCategories(state),
|
||||
diceEnabled: appEnvSelectors.getDiceEnabled(state),
|
||||
classes: rulesEngineSelectors.getClasses(state),
|
||||
species: rulesEngineSelectors.getRace(state),
|
||||
classSpellListSpellsLookup:
|
||||
rulesEngineSelectors.getClassSpellListSpellsLookup(state),
|
||||
dataOriginRefData: rulesEngineSelectors.getDataOriginRefData(state),
|
||||
characterTheme: rulesEngineSelectors.getCharacterTheme(state),
|
||||
};
|
||||
}
|
||||
|
||||
const PreferencesPaneContainer = (props) => {
|
||||
const {
|
||||
pane: { paneHistoryStart },
|
||||
} = useSidebar();
|
||||
return <PreferencesPane paneHistoryStart={paneHistoryStart} {...props} />;
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(PreferencesPaneContainer);
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
import { uniqueId } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
import { Select } from "@dndbeyond/character-components/es";
|
||||
import {
|
||||
HelperUtils,
|
||||
HtmlSelectOption,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading";
|
||||
|
||||
interface Props {
|
||||
initialValue: number | null;
|
||||
onChange?: (value: number | null) => void;
|
||||
onChangePromise?: (
|
||||
newValue: string,
|
||||
oldValue: string,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
) => void;
|
||||
heading: React.ReactNode;
|
||||
className: string;
|
||||
initialOptionRemoved: boolean;
|
||||
options: Array<HtmlSelectOption>;
|
||||
description: string;
|
||||
block: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
interface State {
|
||||
value: number | null;
|
||||
}
|
||||
export default class PreferencesPaneSelectField extends React.PureComponent<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
static defaultProps = {
|
||||
initialValue: null,
|
||||
className: "",
|
||||
initialOptionRemoved: false,
|
||||
block: false,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: props.initialValue,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
prevProps: Readonly<Props>,
|
||||
prevState: Readonly<State>,
|
||||
snapshot?: any
|
||||
): void {
|
||||
const { initialValue } = this.props;
|
||||
|
||||
if (initialValue !== prevProps.initialValue) {
|
||||
this.setState({
|
||||
value: initialValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (value: string): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
let parseValue = HelperUtils.parseInputInt(value);
|
||||
|
||||
if (onChange) {
|
||||
onChange(parseValue);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: parseValue,
|
||||
});
|
||||
};
|
||||
|
||||
handleChangePromise = (
|
||||
newValue: string,
|
||||
oldValue: string,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
): void => {
|
||||
const { onChangePromise } = this.props;
|
||||
|
||||
if (onChangePromise) {
|
||||
onChangePromise(
|
||||
newValue,
|
||||
oldValue,
|
||||
() => {
|
||||
let parseValue = HelperUtils.parseInputInt(newValue);
|
||||
this.setState({
|
||||
value: parseValue,
|
||||
});
|
||||
accept();
|
||||
},
|
||||
reject
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value } = this.state;
|
||||
const {
|
||||
onChangePromise,
|
||||
onChange,
|
||||
block,
|
||||
heading,
|
||||
options,
|
||||
className,
|
||||
initialOptionRemoved,
|
||||
description,
|
||||
placeholder,
|
||||
} = this.props;
|
||||
|
||||
const uId = uniqueId("qry-");
|
||||
|
||||
let classNames: Array<string> = [
|
||||
"ct-preferences-pane__field",
|
||||
"ct-preferences-pane__field--select",
|
||||
className,
|
||||
];
|
||||
if (block) {
|
||||
classNames.push("ct-preferences-pane__field--block");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames.join(" ")}>
|
||||
<div className="ct-preferences-pane__field-summary">
|
||||
<Heading className="ct-preferences-pane__field-heading">
|
||||
<label className="ct-preferences-pane__field-label" htmlFor={uId}>
|
||||
{heading}
|
||||
</label>
|
||||
</Heading>
|
||||
<div className="ct-preferences-pane__field-description">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ct-preferences-pane__field-input">
|
||||
<Select
|
||||
options={options}
|
||||
onChange={onChange ? this.handleChange : undefined}
|
||||
onChangePromise={
|
||||
onChangePromise ? this.handleChangePromise : undefined
|
||||
}
|
||||
value={value}
|
||||
initialOptionRemoved={initialOptionRemoved}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import PreferencesPaneSelectField from "./PreferencesPaneSelectField";
|
||||
|
||||
export default PreferencesPaneSelectField;
|
||||
export { PreferencesPaneSelectField };
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
|
||||
import { Toggle } from "~/components/Toggle";
|
||||
import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading";
|
||||
|
||||
interface Props {
|
||||
initiallyEnabled: boolean;
|
||||
onChange: (isEnabled: boolean) => void;
|
||||
heading: React.ReactNode;
|
||||
description: string;
|
||||
}
|
||||
export default class PreferencesPaneToggleField extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
initiallyEnabled: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { heading, description, initiallyEnabled, onChange } = this.props;
|
||||
|
||||
return (
|
||||
<div className="ct-preferences-pane__field ct-preferences-pane__field--toggle">
|
||||
<div className="ct-preferences-pane__field-heading-input">
|
||||
<Heading className="ct-preferences-pane__field-heading">
|
||||
{heading}
|
||||
<Toggle
|
||||
checked={initiallyEnabled}
|
||||
onClick={onChange}
|
||||
color="themed"
|
||||
aria-label={description}
|
||||
/>
|
||||
</Heading>
|
||||
</div>
|
||||
<div className="ct-preferences-pane__field-summary">
|
||||
{description && (
|
||||
<div className="ct-preferences-pane__field-description">
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import PreferencesPaneToggleField from "./PreferencesPaneToggleField";
|
||||
|
||||
export default PreferencesPaneToggleField;
|
||||
export { PreferencesPaneToggleField };
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { orderBy } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
import { Toggle } from "~/components/Toggle";
|
||||
import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading";
|
||||
|
||||
import { ToggleInfo } from "./typings";
|
||||
|
||||
interface Props {
|
||||
toggles: Array<ToggleInfo>;
|
||||
heading: React.ReactNode;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default class PreferencesPaneTogglesField extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
initiallyEnabled: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { heading, description, toggles } = this.props;
|
||||
|
||||
if (!toggles.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sortedToggles = orderBy(toggles, "sortOrder");
|
||||
|
||||
return (
|
||||
<div className="ct-preferences-pane__field ct-preferences-pane__field--toggle">
|
||||
<div className="ct-preferences-pane__field-summary">
|
||||
<Heading className="ct-preferences-pane__field-heading">
|
||||
{heading}
|
||||
</Heading>
|
||||
{description && (
|
||||
<div className="ct-preferences-pane__field-description">
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ct-preferences-pane__field-toggles">
|
||||
{sortedToggles.map((toggle, idx) => (
|
||||
<div className="ct-preferences-pane__field-toggle" key={idx}>
|
||||
<div className="ct-preferences-pane__field-input">
|
||||
<Toggle
|
||||
checked={toggle.initiallyEnabled}
|
||||
onClick={toggle.onChange}
|
||||
onChangePromise={toggle.onChangePromise}
|
||||
color="themed"
|
||||
aria-label={toggle.label}
|
||||
/>
|
||||
</div>
|
||||
<div className="ct-preferences-pane__field-label">
|
||||
{toggle.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import PreferencesPaneTogglesField from "./PreferencesPaneTogglesField";
|
||||
|
||||
export * from "./typings";
|
||||
export default PreferencesPaneTogglesField;
|
||||
export { PreferencesPaneTogglesField };
|
||||
@@ -0,0 +1,12 @@
|
||||
import PreferencesPane from "./PreferencesPane";
|
||||
import PreferencesPaneSelectField from "./PreferencesPaneSelectField";
|
||||
import PreferencesPaneToggleField from "./PreferencesPaneToggleField";
|
||||
import PreferencesPaneTogglesField from "./PreferencesPaneTogglesField";
|
||||
|
||||
export default PreferencesPane;
|
||||
export {
|
||||
PreferencesPane,
|
||||
PreferencesPaneSelectField,
|
||||
PreferencesPaneToggleField,
|
||||
PreferencesPaneTogglesField,
|
||||
};
|
||||
Reference in New Issue
Block a user